【问题标题】:Metaprogramming trick to sort out private functions整理私有函数的元编程技巧
【发布时间】:2015-04-17 20:46:18
【问题描述】:

这是一个关于 C++ 编译时自省的问题

是否有已知的技巧来检测匹配函数实际上是私有的,因此不能被调用?我的直觉猜测是,这是不可能——但也许我错了?


说明:使用元编程技术(主要基于 SFINAE 机制),可以检测作为模板参数给出的类型(即我们知道这种类型)中是否存在属性(嵌套类型、成员、具有特定签名的函数)存在,但我们对该类型一无所知)。基于这样的自省谓词,就可以创建专门的实现、适配器等。

现在具体的转折是这样的谓词可能匹配一个类的私有成员。当我们随后根据该(假)匹配的结果生成代码时,编译失败,因为我们实际上不允许访问该成员。因此,解决这种错误匹配的技术会很好......

【问题讨论】:

  • 违反访问控制确实会导致 C++11 中的替换失败。
  • 情况令人困惑,可能是由于编译器支持。在 GCC 4.7 上编译时,std::is_copy_assignable<MyType> 会引发错误“operator= is private .... within this context”。罪证表达式在 type_traits, 1049 (__is_assignable_helper) 中:decltype(declval<MyType&>() = declval<MyType const&>(), __one())

标签: c++ c++11 metaprogramming


【解决方案1】:

好的,我从不使用它。这只是一个想法。

所以,为了检测函数是否存在,我们需要这样的东西:

#include <iostream>

#define DEFINE_METHOD_CHECKER(RETURN_TYPE, METHOD_NAME, PARAMETERS)     \
template<typename T>                                                    \
struct Is ## METHOD_NAME ## MemberFunctionExists                        \
{                                                                       \
private:                                                                \
    typedef char True;                                                  \
    typedef char (&False)[2];                                           \
    template<typename U, RETURN_TYPE (U::*)PARAMETERS = &U::METHOD_NAME>\
    struct Checker                                                      \
    {                                                                   \
        typedef True Type;                                              \
    };                                                                  \
    template<typename U>                                                \
    static typename Checker<U>::Type Tester(const U*);                  \
    static False Tester(...);                                           \
public:                                                                 \
    enum { value = (sizeof(Tester(static_cast<const T*>(0))) == sizeof(True)) }; \
}

// IsMethodMemberFunctionExists<T>::value
DEFINE_METHOD_CHECKER(int, Method, (bool));
// IsTestMemberFunctionExists<T>::value
DEFINE_METHOD_CHECKER(int*, Test, (int&, char));

class Exists
{
public:
    int Method(bool);
    int* Test(int&, char);
};

class NotExists
{
};

int main()
{
   std::cout << IsMethodMemberFunctionExists<Exists>::value << std::endl;
    std::cout << IsTestMemberFunctionExists<Exists>::value << std::endl;

    std::cout << IsMethodMemberFunctionExists<NotExists>::value << std::endl;
    std::cout << IsTestMemberFunctionExists<NotExists>::value << std::endl;
}


Output:
1
1
0
0

Live

很好。如果我要评论 public: - 函数将是私有的,我们的检测器将打印 0 - 这样的成员函数不存在(“违反访问控制确实会导致 C++11 中的替换失败。” 正如@T.C. 所说):

class Exists
{
//public:
    int Method(bool);
    int* Test(int&, char);
};

Output:
0
0
0
0

Live

现在,为了检测,如果给定的函数是私有的,我们将通过抽象类使其公开。简而言之:

struct ITest
{
    virtual void foo() = 0;
};

class Test : ITest
{
    // foo() is private AND it's override Itest::foo() !
    void foo();
}

// std::is_abstract<Test>() yields false !

所以,一起来

#define DEFINE_PRIVATE_METHOD_CHECKER(RETURN_TYPE, METHOD_NAME, PARAMETERS) \
DEFINE_METHOD_CHECKER(RETURN_TYPE, METHOD_NAME, PARAMETERS); \
template<typename T> \
struct IsPrivate ## METHOD_NAME ## MemberFunctionExists \
{ \
public: \
    struct IOverrideTest \
    { \
        virtual RETURN_TYPE METHOD_NAME PARAMETERS = 0; \
    }; \
    \
    class OverrideTest : T, IOverrideTest {}; \
    \
public: \
    enum { value = !IsMethodMemberFunctionExists<T>::value && !std::is_abstract<OverrideTest>::value }; \
}

// IsPrivateMethodMemberFunctionExists<T>::value
DEFINE_PRIVATE_METHOD_CHECKER(int, Method, (bool));

#include <iostream>

class PrivateExists
{
private:
    int Method(bool);
    int* Test(int&, char);
};

class NotExists
{
};

int main()
{   
    std::cout << IsPrivateMethodMemberFunctionExists<PrivateExists>::value << std::endl;
    std::cout << IsMethodMemberFunctionExists<PrivateExists>::value << std::endl;

    //std::cout << IsPrivateMethodMemberFunctionExists<NotExists>::value << std::endl;
    //std::cout << IsMethodMemberFunctionExists<NotExists>::value << std::endl;
}

代码看起来合法,但是:

Clang Live 将产生预期的输出:

1
0

gcc Live:

0
0

VC++ Live不会编译它。

嗯.. 下一个代码未注释,事情变得很奇怪:

int main()
{   
    std::cout << IsPrivateMethodMemberFunctionExists<PrivateExists>::value << std::endl;
    std::cout << IsMethodMemberFunctionExists<PrivateExists>::value << std::endl;

    std::cout << IsPrivateMethodMemberFunctionExists<NotExists>::value << std::endl;
    std::cout << IsMethodMemberFunctionExists<NotExists>::value << std::endl;
}

Clang Live:

1
0
1
0

gcc Live

0
0
0
0

更新:我有一个关于覆盖的错误:

class Exists
{
    void foo();
};

struct ITest
{
    virtual void foo() = 0;
};

class TestExists : Exists, ITest
{
};

TestExists 不会覆盖foo(),所以它仍然是抽象的。解决方案是错误的。 Bu 编译器的结果很有趣...

【讨论】:

  • 不同之处是……相当有趣。这似乎是由嵌套类引起的:coliru.stacked-crooked.com/a/f418a969d55ec68f——它看起来确实很像一个错误。
  • TestExists 需要一个using Exists::foo
  • @dascandy,我不能使用 using-declaration 因为,实际上,我不知道,如果函数存在(在通用代码中).. 这个函数是私有的
猜你喜欢
  • 1970-01-01
  • 2011-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-07
  • 1970-01-01
  • 1970-01-01
  • 2016-01-04
相关资源
最近更新 更多