gpt4 book ai didi

c++ - 可变参数模板仅在前向声明时编译

转载 作者:IT老高 更新时间:2023-10-28 22:14:58 25 4
gpt4 key购买 nike

我有一个继承自所有模板参数的可变参数模板:

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,我可以成功地使用不完整的类型。已被前向声明,但不是(如果已定义)。

为什么这个代码是 not compile :

#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");
}

而此代码 does compile :

#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's observation一针见血:这里的问题是 ADL。这实际上是我遇到的相同问题 with this question .

问题是这样的:我们这里有一个不合格的电话:

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

关于c++ - 可变参数模板仅在前向声明时编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56702968/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com