Constexpr 关于成员函数指针值 - 未定义的行为?

mik*_*ikk 6 c++ templates c++11 c++14

我对 C++ 中的简单反射机制有问题。我想要一种模板类型,它应该以不同的成员函数指针作为模板参数表现不同:

[解决方案#1,按照标准是不好的]

如果我有一个带有类类型及其成员函数指针的类模板,我不能部分地专门化为空的成员指针,因为我不能专门化“具有依赖类型的非类型模板参数”(请参阅​​:https://en。 cppreference.com/w/cpp/language/partial_specialization参数列表 [5])

template<class O, void(O::*foo)() = nullptr>
struct p
{};

template<class O, void(O::*foo)()>
struct p<O, nullptr>
{};
Run Code Online (Sandbox Code Playgroud)

[解决方案#2,GCC 问题]

如果我尝试专门研究一个推导的 constexpr 值,该值反映到成员指针,编译器会遇到不同的问题。GCC8.2 x64 告诉我nullptr == foo表达式在上下文中不是常量:p<A, &A::f> j;. 但是ARM GCC8说没关系。我认为这是一些内存布局问题,struct A在模板引擎尝试评估具体函数指针时不完整。

template<class O, void(O::*foo)() = nullptr, bool = nullptr == foo>
struct p
{};

template<class O, void(O::*foo)()>
struct p<O, foo, true>
{};

struct A
{
    void f();
    p<A, &A::f> j;
};
Run Code Online (Sandbox Code Playgroud)

[解决方案#2 用法,MSVC 问题]

奇怪的是,MSVC 19.5 x86 对上面的没问题,它还有另一个问题。它正在工作,而结构 A 是独立的,或者它继承自一个结构。但是当有两个基本结构(X,Y)时,它会因内部编译器错误而死亡。

template<class O, void(O::*foo)() = nullptr, bool = nullptr == foo>
struct p
{};

template<class O, void(O::*foo)()>
struct p<O, foo, true>
{};

struct X{};
struct Y{};

struct A : X, Y
{
    void f();
    p<A> i;
    p<A, &A::f> j;
};
Run Code Online (Sandbox Code Playgroud)

Clang7 可以很好地处理上述所有问题。

任何人都可以解释一下,如果这是未定义的行为,或者标准中是否有关于此主题的任何指南?

Bar*_*rry 0

这是CWG 2127。基本上,我们在[temp.class.spec]中有这个限制:

与专门化的非类型参数相对应的模板参数的类型不应依赖于专门化的参数。

nullptr这基本上意味着我们不能直接做你想做的那种专业化。这是……蹩脚的。但是编程中的任何问题都可以通过添加另一层间接来解决,对吧?

template<class O, void(O::*foo)(), bool is_null>
struct p_impl { /* non-null case */ };

template<class O, void(O::*foo)()>
struct p_impl<O, foo, true> { /* null case */ };

template<class O, void(O::*foo)() = nullptr>
using p = p_impl<O, foo, foo == nullptr>;
Run Code Online (Sandbox Code Playgroud)

另一方面,gcc 在比较指向不完整类型成员的指针时似乎存在问题。它拒绝这一点:

template <bool> struct Z { };

struct C
{
    void f();
    Z<&C::f == nullptr> z; // not a constant expression
};
Run Code Online (Sandbox Code Playgroud)

我认为这是gcc bug 56428