我一直在尝试一种用于可组合管道的系统,该系统涉及一组“阶段”,这些阶段可以进行模板化。每个阶段都处理自己的设置,执行和清除,模板推导用于构建管道使用的“状态”的最小列表。这需要很多样板模板代码,这些代码显示出一些明显不协调的行为。尽管进行了成功的实验,但实际上将其滚动到我们的代码库中仍会由于无效的实例化而导致错误。
花了一些时间来追踪玩具(工作)解决方案与功能更丰富的版本之间的区别,但最终将其缩小为一个明确的命名空间规范。
template<typename KeyType = bool>
struct bind_stage
{
static_assert(!std::is_same<KeyType, bool>::value, "Nope, someone default instantiated me");
};
template<typename BoundStage, typename DefaultStage>
struct test_binding {};
template<template<typename...>class StageTemplate, typename S, typename T>
struct test_binding <StageTemplate<S>, StageTemplate<T>> {};
template<typename T>
auto empty_function(T b) {}
然后我们的主要对象:
int main()
{
auto binder = test_binding<bind_stage<int>, bind_stage<>>();
//empty_function(binder); // Fails to compile
::empty_function(binder); // Compiles happily
return 0;
}
现在,我不确定是否会失败。一方面,我们创建了一个test_binder<bind_stage<int>,bind_stage<bool>>
,其中显然包含无效的实例化bind_stage<bool>
作为其类型定义的一部分。哪个应该无法编译。
另一方面,它只是作为名称而不是定义包含在内。在这种情况下,它可能只是一个前向声明的模板,并且我们希望它可以工作,只要外部模板中没有任何东西专门针对它进行引用。
我没有想到的是两种不同的行为,具体取决于我是否添加了(理论上是多余的)全局名称空间说明符。
我已经在Visual Studio,Clang和GCC中尝试了此代码。所有人都有相同的行为,这使我摆脱了编译器错误。这种行为是C ++标准中的某种解释吗?
编辑: Daniel Langr的另一个示例对我来说意义不大:
template <typename T>
struct X {
static_assert(sizeof(T) == 1, "Why doesn't this happen in both cases?");
};
template <typename T>
struct Y { };
template <typename T>
void f(T) { }
int main() {
auto y = Y<X<int>>{};
// f(y); // triggers static assertion
::f(y); // does not
}
在定义X<int>
时实例化Y<X<int>>
或不实例化。在未指定范围内使用功能与什么有什么关系?
答案 0 :(得分:5)
在需要时实例化模板。那么,为什么当一个人像f(Y<X<int>> {});
执行一个非限定调用时,编译器会实例化X<int>
却不象对f
的调用那样实例化::f(X<Y<int>>{})
呢?
原因是依赖于参数的名称查找(ADL)(请参阅MongoDB.Entities)。
在调用f(Y<X<int>>{})
的情况下,编译器必须在X<int>
的定义中查找朋友功能的声明:
template <typename T>
struct X {
//such function will participate to the overload resolution
//to determine which function f is called in "f(Y<X<int>>{})"
friend void f(X&){}
};
涉及专门化的模板参数类型(即函数参数类型(ouch ...))的ADL非常不受欢迎(因为它几乎只会引起严重的意外),因此建议删除它:[basic.lookup.argdep]