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

时间:2019-01-07 20:23:52

标签: c++ c++11 templates c++14

我在使用C ++中的简单反射机制时遇到问题。我想拥有一种模板类型,它在使用不同的成员函数指针作为模板参数时应具有不同的行为:

[解决方案1,按标准,这很糟糕]

如果我有一个带有类类型及其成员函数指针的类模板,则由于不能将“具有依赖类型的非类型模板参数”特化,所以不能部分专门化为null的成员指针。(请参阅:{{3 }}参数列表[5])

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

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

[解决方案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;
};

[解决方案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;
};

Clang7可以很好地满足上述所有要求。

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

2 个答案:

答案 0 :(得分: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>;
另一方面,

gcc似乎在比较指向不完整类型成员的指针时遇到问题。它拒绝这样做:

template <bool> struct Z { };

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

我想这是gcc bug 56428

答案 1 :(得分:0)

这里的问题是,您正在将函数指针(不是常数,因为它可能仅在链接时才知道)与nullptr(即)进行比较。

您可以通过使用std::integral_constant生成此类函数类型的相对常数来解决此问题,然后可以对其进行比较。

所以(对于基本 C ++ 11 )类似:

template<class O>
using F = void(O::*)();

template<class O>
using NullF = std::integral_constant<F<O>, nullptr>;

template<class O, F<O> foo = nullptr,
    bool = std::is_same<std::integral_constant<F<O>, foo>, NullF<O>>::value>
struct p
{};

template<class O, F<O> foo>
struct p<O, foo, true>
{};

struct A
{
    void f();
    p<A, &A::f> j;
    p<A> k;
};

使用 C ++ 17 ,您可以仅使用带有if constexpr检查功能:

template<class O>
using F = void(O::*)();

template<class O>
using NullF = std::integral_constant<F<O>, nullptr>;

template<class O, F<O> foo = nullptr>
struct p
{
    p()
    {
        if constexpr (std::is_same_v<std::integral_constant<F<O>, foo>, NullF<O>>)
        {
            // yay
        }
    }
};

struct A
{
    void f();
    p<A, &A::f> j;
    p<A> i;
};