【问题标题】:Clang issue: Detecting constexpr function pointer with SFINAEClang 问题:使用 SFINAE 检测 constexpr 函数指针
【发布时间】:2014-03-10 02:46:56
【问题描述】:

根据Detecting constexpr with SFINAE 中的答案,我正在尝试使用 SFINAE 检查我的班级中是否存在“constexpr”。

问题是constexpr是一个函数指针:

#include <type_traits>
#include <iostream>

typedef int (*ptr_t)();
int bar() { return 9; }

struct Foo {
    static constexpr ptr_t ptr = &bar;
};

namespace detail {
template <ptr_t>
struct sfinae_true : std::true_type {};

template <class T>
sfinae_true<T::ptr> check(int);

// Commented out to see why clang was not evaluating to true. This should only be
// a comment when debugging!
// template <class>
// std::false_type check(...);
}  // detail::

template <class T>
struct has_constexpr_f : decltype(detail::check<T>(0)) {};

int main(int argc, char *argv[]) {
    std::cout << has_constexpr_f<Foo>::value << std::endl;
    return 0;
}

使用 gcc 似乎可以正常工作,但是 clang 抱怨:

test.cxx:23:39: error: no matching function for call to 'check'
    struct has_constexpr_f : decltype(detail::check<T>(0)) {};
                                      ^~~~~~~~~~~~~~~~
test.cxx:26:22: note: in instantiation of template class 'has_constexpr_f<Foo>' requested here
        std::cout << has_constexpr_f<Foo>::value << std::endl;
                     ^
test.cxx:16:25: note: candidate template ignored: substitution failure [with T = Foo]: non-type template argument for template parameter of pointer type 'ptr_t' (aka 'int (*)()') must have its address taken
    sfinae_true<T::ptr> check(int);
                ~       ^
1 error generated.

Q1:任何人都可以提出一种适用于 Clang 和 GCC 的方法吗?

Q2:这是 gcc、clang 中的错误还是在 c++ 标准中未定义?

【问题讨论】:

    标签: c++11 template-meta-programming


    【解决方案1】:

    这不是 clang 中的错误,而是对指针类型的非类型模板参数的参数的不幸限制(请参阅pointer as non-type template argument)。本质上,您只能使用 &amp;something: [temp.arg.nontype]/1 (from n3797) 形式的参数

    [如果模板参数是一个指针,它的参数可以是]一个常量表达式(5.19),它指定一个地址 具有静态存储持续时间和外部或 内部链接或具有外部或内部链接的功能, 包括函数模板和函数 template-ids 但不包括 非静态类成员,表示(忽略括号)为&amp; id-expression,其中 id-expression 是对象的名称或 函数,除了如果名称指的是一个 & 可以省略 函数或数组,如果对应的则应省略 模板参数是一个参考;或 [..]

    [强调我的]

    但是,您可以在具有非指针类型的常量表达式中使用函数指针,例如布尔表达式,例如

    T::ptr != nullptr
    

    这适用于clang++3.5和g++4.8.2:

    #include <type_traits>
    #include <iostream>
    
    typedef int (*ptr_t)();
    int bar() { return 9; }
    
    struct Foo0 {
        static constexpr ptr_t ptr = &bar;
    };
    
    struct Foo1 {
        static const ptr_t ptr;
    };
    ptr_t const Foo1::ptr = &bar;
    
    struct Foo2 {
        static const ptr_t ptr;
    };
    //ptr_t const Foo2::ptr = nullptr;
    
    namespace detail
    {
        template <bool>
        struct sfinae_true : std::true_type {};
    
        template <class T>
        sfinae_true<(T::ptr != nullptr)> check(int);
        // the result of the comparison does not care
    
        template <class>
        std::false_type check(...);
    }  // detail::
    
    template <class T>
    struct has_constexpr_f : decltype(detail::check<T>(0)) {};
    
    int main(int argc, char *argv[]) {
        std::cout << std::boolalpha << has_constexpr_f<Foo0>::value << std::endl;
        std::cout << std::boolalpha << has_constexpr_f<Foo1>::value << std::endl;
        std::cout << std::boolalpha << has_constexpr_f<Foo2>::value << std::endl;
        return 0;
    }
    

    请注意,对于第二个输出 (Foo1),clang++ 和 g++ 之间存在差异:g++ 表示 true,clang++ 表示 false

    【讨论】:

    • 如果您愿意,可以添加类似std::is_same&lt; decltype(T::ptr), ptr_t &gt; 的支票。这不会检查 T::ptr 是否为常量表达式。
    猜你喜欢
    • 2013-02-20
    • 1970-01-01
    • 2020-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-05
    相关资源
    最近更新 更多