传递指针参数时意外的模板实例化

Unexpected template instantiation when passing a pointer argument

以下代码无法编译(在 godbolt.org 上使用带有 /std:c++latest 的 MSVC 19.28 和带有 -std=c++20 的 Clang 12.0 进行测试):

#include <variant>

struct some_thing;
using var = std::variant<some_thing>;

// There should be NO need to instantiate the template here, right?
void take(var* v) {}

int main() {
    var* v = nullptr;
    take(v); // This is the offending line - tries to instantiate the variant template and fails as 'some_thing' is undefined up until now.
    return 0;
}

对 main 的调用如何需要模板实例化?我们只是传递一个指针。这是一个错误还是我遗漏了什么?

Template instantiation should not occur if working with a pointer type of the template (as I'm doing it) and not when typedef'ing 它作为 typedef 不引入新类型(即模板实例)。我可能仍然遗漏了一些东西,但这应该使我的示例代码能够编译。

请参阅下面的 MSVC 错误消息。请注意,此问题不是关于理解错误消息的问题。它关于为什么甚至首先发生模板实例化。

C:/data/msvc/14.28.29333/include\type_traits(807): error C2139: 'some_thing': an undefined class is not allowed as an argument to compiler intrinsic type trait '__is_trivially_destructible'
<source>(3): note: see declaration of 'some_thing'
C:/data/msvc/14.28.29333/include\variant(956): note: see reference to class template instantiation 'std::is_trivially_destructible<some_thing>' being compiled
C:/data/msvc/14.28.29333/include\variant(956): note: see reference to variable template 'const bool conjunction_v<std::is_trivially_destructible<some_thing> >' being compiled
C:/data/msvc/14.28.29333/include\variant(1014): note: see reference to alias template instantiation 'std::_Variant_destroy_layer<some_thing>' being compiled
<source>(11): note: see reference to class template instantiation 'std::variant<some_thing>' being compiled
C:/data/msvc/14.28.29333/include\type_traits(661): error C2139: 'some_thing': an undefined class is not allowed as an argument to compiler intrinsic type trait '__is_constructible'
<source>(3): note: see declaration of 'some_thing'
C:/data/msvc/14.28.29333/include\xsmf_control.h(61): note: see reference to class template instantiation 'std::is_move_constructible<some_thing>' being compiled
C:/data/msvc/14.28.29333/include\xsmf_control.h(62): note: see reference to variable template 'const bool conjunction_v<std::is_move_constructible<some_thing>,std::negation<std::conjunction<std::is_trivially_move_constructible<some_thing> > > >' being compiled
C:/data/msvc/14.28.29333/include\xsmf_control.h(100): note: see reference to alias template instantiation 'std::_SMF_control_move<std::_Variant_destroy_layer_<some_thing>,some_thing>' being compiled
C:/data/msvc/14.28.29333/include\xsmf_control.h(139): note: see reference to alias template instantiation 'std::_SMF_control_copy_assign<std::_Variant_destroy_layer_<some_thing>,some_thing>' being compiled
C:/data/msvc/14.28.29333/include\xsmf_control.h(146): note: see reference to alias template instantiation 'std::_SMF_control_move_assign<std::_Variant_destroy_layer_<some_thing>,some_thing>' being compiled
C:/data/msvc/14.28.29333/include\variant(1014): note: see reference to alias template instantiation 'std::_SMF_control<std::_Variant_destroy_layer_<some_thing>,some_thing>' being compiled
C:/data/msvc/14.28.29333/include\type_traits(630): error C2139: 'some_thing': an undefined class is not allowed as an argument to compiler intrinsic type trait '__is_constructible'
<source>(3): note: see declaration of 'some_thing'
C:/data/msvc/14.28.29333/include\xsmf_control.h(39): note: see reference to class template instantiation 'std::is_copy_constructible<some_thing>' being compiled
C:/data/msvc/14.28.29333/include\xsmf_control.h(40): note: see reference to variable template 'const bool conjunction_v<std::is_copy_constructible<some_thing>,std::negation<std::conjunction<std::is_trivially_copy_constructible<some_thing> > > >' being compiled
C:/data/msvc/14.28.29333/include\xsmf_control.h(63): note: see reference to alias template instantiation 'std::_SMF_control_copy<std::_Variant_destroy_layer_<some_thing>,some_thing>' being compiled
C:/data/msvc/14.28.29333/include\variant(491): error C2079: 'std::_Variant_storage_<false,some_thing>::_Head' uses undefined struct 'some_thing'
C:/data/msvc/14.28.29333/include\variant(838): note: see reference to class template instantiation 'std::_Variant_storage_<false,some_thing>' being compiled
C:/data/msvc/14.28.29333/include\variant(941): note: see reference to class template instantiation 'std::_Variant_base<some_thing>' being compiled
C:/data/msvc/14.28.29333/include\xsmf_control.h(85): note: see reference to class template instantiation 'std::_Variant_destroy_layer_<some_thing>' being compiled
C:/data/msvc/14.28.29333/include\xsmf_control.h(124): note: see reference to class template instantiation 'std::_Deleted_copy_assign<std::_Variant_destroy_layer_<some_thing>,some_thing>' being compiled
C:/data/msvc/14.28.29333/include\variant(1014): note: see reference to class template instantiation 'std::_Deleted_move_assign<std::_Variant_destroy_layer_<some_thing>,some_thing>' being compiled
C:/data/msvc/14.28.29333/include\type_traits(730): error C2139: 'some_thing': an undefined class is not allowed as an argument to compiler intrinsic type trait '__is_destructible'
<source>(3): note: see declaration of 'some_thing'
C:/data/msvc/14.28.29333/include\variant(1016): note: see reference to class template instantiation 'std::is_destructible<some_thing>' being compiled
C:/data/msvc/14.28.29333/include\variant(1016): note: see reference to variable template 'const bool conjunction_v<std::is_object<some_thing>,std::negation<std::is_array<some_thing> >,std::is_destructible<some_thing> >' being compiled
C:/data/msvc/14.28.29333/include\variant(1016): error C2338: variant<Types...> requires all of the Types to meet the Cpp17Destructible requirements N4828 [variant.variant]/2.
Compiler returned: 2

您的问题与ADL有关。

来自link:

Otherwise, for every argument in a function call expression its type is examined to determine the associated set of namespaces and classes that it will add to the lookup.

检查将导致函数调用中使用的模板的实例化,如果在这种情况下 std::variant 需要 some_thing 的类型定义,则会失败。

我们知道 ADL 是问题所在,在这种情况下我们可以简单地抑制 ADL,如果我们知道我们不想在其他名称空间或范围中查找以找到为 take() 定义的函数std::variant<some_thing> 的类型,使用 :: operator.

如果您通过将格式错误的行替换为 ::take(v); 来更改代码,您的代码将编译,因为 ADL 将不再发生。