nok*_*okz 8 c++ language-lawyer compiler-bug c++20 non-type-template-parameter
考虑这段代码:
#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);
}
Run Code Online (Sandbox Code Playgroud)
上面的代码在 Clang 中编译失败,并出现如下错误,但在 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;
| ^
Run Code Online (Sandbox Code Playgroud)
这里哪个编译器是正确的?
我相信 GCC 在这里应该是正确的,因为上面的代码在纸面上应该是合法的,但是,我不确定情况是否确实如此。
此外,如果我将特征的定义更改is_wrapper_object为:
<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;
| ^
Run Code Online (Sandbox Code Playgroud)
海湾合作委员会是正确的。
请注意,只需将您替换auto&为即可const auto&使代码针对两者进行编译。无论问题是什么,它都与部分特化中的占位符类型说明符有关。
注意:
如果模板参数
T的类型包含占位符类型或推导类类型的占位符 ([dcl.type.class.deduct]),则参数的类型是为发明声明中的变量推导的类型xRun Code Online (Sandbox Code Playgroud)T x = E ;
换句话说,auto&模板中的参数应该是const int&和const wrapper&之后这样的推导。用户不需要const自己提供。
我无法找到相关的 LLVM 错误报告,因此我提交了一份: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
}
Run Code Online (Sandbox Code Playgroud)
由此可见,clang完全无法Value在 的部分特化范围内进行推导test。