可变参数模板仅在正向声明时编译
Variadic template only compiles when forward declared
我有一个从所有模板参数继承的可变参数模板:
template <typename... Ts>
struct derived : Ts...
{
};
我还想有一个工具来表达"添加模板参数的现有derived
"的类型。我对此的尝试是:
// Do not ODR-use (goes in namespace impl or similar)!
template<class ... NewInputs, class ... ExistingInputs>
auto addedHelper(const derived<ExistingInputs...>&)
-> derived<ExistingInputs..., NewInputs...>;
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
举个简单的例子,Added<derived<A, B>, C>
应该是derived<A, B, C>
。我使用辅助函数对第一个参数包进行模板参数推导。
我的问题:出于某种原因,如果已向前声明derived
我可以成功地将其用于不完整的类型,但如果已定义,则不能。
为什么这段代码不编译:
#include <utility>
template <typename... Ts>
struct derived : Ts...
{};
template<class ... NewInputs, class ... ExistingInputs>
auto addedHelper(const derived<ExistingInputs...>&)
-> derived<ExistingInputs..., NewInputs...>;
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
struct A;
struct B;
struct C;
// Goal: This forward declaration should work (with incomplete A, B, C).
auto test(derived<A, B> in) -> Added<decltype(in), C>;
struct A {};
struct B {};
struct C {};
void foo()
{
auto abc = test({});
static_assert(std::is_same_v<decltype(abc), derived<A, B, C>>, "Pass");
}
而这段代码确实可以编译:
#include <utility>
template <typename... Ts>
struct derived;
template<class ... NewInputs, class ... ExistingInputs>
auto addedHelper(const derived<ExistingInputs...>&)
-> derived<ExistingInputs..., NewInputs...>;
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
struct A;
struct B;
struct C;
// Goal: This forward declaration should work (with incomplete A, B, C).
auto test(derived<A, B> in) -> Added<decltype(in), C>;
template <typename... Ts>
struct derived : Ts...
{};
struct A {};
struct B {};
struct C {};
void foo()
{
auto abc = test({});
static_assert(std::is_same_v<decltype(abc), derived<A, B, C>>, "Pass");
}
为方便起见,这里同时是两种情况(注释输入/注释#define FORWARD_DECLARED
): https://godbolt.org/z/7gM52j
我不明白代码如何通过用相应的定义替换前向声明而变得非法(否则稍后会出现)。
Evg 的观察一针见血:这里的问题是 ADL。这实际上与我在这个问题上遇到的问题相同。
问题是:我们这里有一个不合格的电话:
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
// ^^^^^^^^^^^
我们知道它是一个函数模板,因为我们在使用常规查找时找到了它,所以我们不必处理整个"<
运算符还是模板介绍器"的问题。但是,由于这是一个非限定调用,因此我们还必须执行依赖于参数的查找。
ADL 需要查看所有参数的关联命名空间,这似乎很好 - 我们不需要完整的类型。但 ADL还需要查找潜在的友元函数和类中定义的函数模板。毕竟,这需要工作:
struct X {
friend void foo(X) { }
};
foo(X{}); // must work, call the hidden friend defined within X
因此,在我们的问题呼吁中:
auto test(derived<A, B> in) -> Added<decltype(in), C>;
我们必须实例化derived<A, B>
...但是这种类型继承自两个不完整的类,我们不能这样做。这就是问题所在,这就是我们失败的地方。
这就是前向声明版本工作的原因。template <typename... T> struct derived;
是不完整的,所以只是试图在里面寻找朋友函数,什么也找不到 - 我们不需要实例化任何其他东西。
同样,derived
完整但实际上并非从任何东西派生的版本也可以工作。
值得庆幸的是,在这种情况下,Evg 的建议可以解决这个问题。拨打合格电话:
template<class ExistingInput, class ... NewInputs>
using Added = decltype(::addedHelper<NewInputs...>(std::declval<ExistingInput>()));
这避免了您甚至不想要的 ADL。最好的情况是,你避免做一些对你没有好处的事情。不好的情况,你的代码无法编译。糟糕的情况,对于某些输入,您不小心完全调用了不同的函数。
或者只是使用Boost.Mp11的mp_push_back
- 是什么原因导致它无法编译?它是声明签名还是在函数本身的实现中
- 未声明的标识符编译暗黑破坏神 2 程序"muleview"
- 模板方法访问正向声明的类仅在没有此指针的情况下无法编译
- 使用直接大括号初始化时,C++ 编译错误"声明末尾的预期";"
- 即使不包含其标头,如何成功向前声明的类编译?
- 如何制作 cmakelists.txt编译使用在其他地方声明和实现的函数和类的 CPP
- C++ 在编译过程中 strtok 函数 Eclipse 说没有在范围内声明?
- Lamda 仅在声明为"自动"时编译,而不是在声明为'bool'时编译
- 由于值返回函数中的错误,程序无法编译.它说未声明的标识符
- C++ SPDLOG 编译错误:变量或字段"set_error_handler"声明为无效
- 在 mingw64- 变量下的窗口中编译 openvpn3 时出错,未在范围内声明
- 尝试编译GoogleTest,但出现以下错误:尚未声明'::OpenThread'
- 如何实现声明功能-C 11,编译时间
- 可变参数模板仅在正向声明时编译
- std::d eclare_if 或其他在编译时丢弃成员声明的假设方法
- 编译错误 在 C++ 上,Calcarea 未在此范围内声明
- 试图过渡到GTK3(针对GTK -3.0库编译)时,在此范围内未声明GTK_Object
- 声明/编译向量中的2d向量
- 需要不完整的类型/正向声明/编译理解
- 使用前向声明编译时"null"?