在C++20中,是否可以通过非类型模板参数使用trait来确定对象是否属于类类型?

zaqlnxep  于 6个月前  发布在  其他
关注(0)|答案(1)|浏览(74)

请考虑以下代码:

#include <type_traits>

template <typename T>
struct wrapper {
    T& ref;
    constexpr wrapper(T& ref) : ref(ref) {}
};

// Trait that checks whether a type is of the form `wrapper<T>`
template <typename T>
struct is_wrapper_type : std::false_type {};

template <typename T>
struct is_wrapper_type<wrapper<T>> : std::true_type {};

// Trait that checks whether an object is of the type `wrapper<T>`
template <auto& Value>
struct is_wrapper_object;

template <auto& Value>
    requires (!is_wrapper_type<std::decay_t<decltype(Value)>>::value)
struct is_wrapper_object<Value> : std::false_type {};

template <auto& Value>
    requires is_wrapper_type<std::decay_t<decltype(Value)>>::value
struct is_wrapper_object<Value> : std::true_type {};

int main() {
    static constexpr int v = 42;
    static_assert(!is_wrapper_object<v>::value);
    static constexpr wrapper w {v};
    static_assert(is_wrapper_object<w>::value);
}

字符串
上面的fails to compile in Clang with the errors shown below but compiles successfully in GCC

<source>:30:20: error: implicit instantiation of undefined template 'is_wrapper_object<v>'
   30 |     static_assert(!is_wrapper_object<v>::value);
      |                    ^
<source>:18:8: note: template is declared here
   18 | struct is_wrapper_object;
      |        ^
<source>:32:19: error: implicit instantiation of undefined template 'is_wrapper_object<w>'
   32 |     static_assert(is_wrapper_object<w>::value);
      |                   ^
<source>:18:8: note: template is declared here
   18 | struct is_wrapper_object;
      |        ^


这里哪个编译器是正确的?
我相信GCC在这里应该是正确的,因为上面的代码 * 应该 * 在纸面上是法律的的,但是,我不确定是否真的是这样。
此外,如果我将特征is_wrapper_object的定义改为:

template <auto& Value>
struct is_wrapper_object : std::false_type {};

template <auto& Value>
    requires is_wrapper_type<std::decay_t<decltype(Value)>>::value
struct is_wrapper_object<Value> : std::true_type {};


compiles on Clang as well, albeit with wrong output .

w1e3prcc

w1e3prcc1#

GCC是正确的。
请注意,只需将const auto&替换为const auto&,代码就可以同时编译。无论问题是什么,它都与部分专门化中的占位符类型说明符有关。
请注意:
如果模板参数的类型T包含placeholder type或推导类类型的占位符([dcl.type.class. defect]),则参数的类型是在本发明的声明中为变量x推导的类型

T x = E ;

字符串

  • [临时参数非类型] p1
    也就是说,模板参数中的auto&经过这样的推导后,应该是const int&const wrapper&,而不需要用户自己提供const
    我找不到相关的LLVM bug报告,所以我提交了一个:LLVM Bug 77189

最小可复制示例

参见https://godbolt.org/z/rhKsWKdPz

#include <type_traits>

template <auto& Value, int>
struct test : std::false_type {};

template <auto& Value>
struct test<Value, 0> : std::true_type {};

int main() {
    static constexpr int v = 42;
    static_assert(test<v, 0>::value); // fails for clang
}


由此,我们可以看出clang * 完全 * 无法在test的部分特化中推导出Value

相关问题