gpt4 book ai didi

c++ - 构建父级的可变参数模板构造函数

转载 作者:行者123 更新时间:2023-11-30 04:04:58 25 4
gpt4 key购买 nike

我试图从一堆我知道接口(interface)的类继承,但是它们可能有非常不同的构造函数。为此,我决定在派生类构造函数中使用可变参数模板,以便它可以获得最终将提供给父类的任意参数。

我的代码如下:

#include <iostream>

struct A {
A(int) { std::cout << "This is int\n"; }
A(unsigned) { std::cout << "This is unsigned\n"; }
};

struct B {
B(char) { std::cout << "This is char\n"; }
};

template <typename T>
struct C : public T {
template <typename... Args>
C(double, Args&&... params) : T(std::forward<Args>(params)...) { std::cout << "This is double\n"; }
// But what about this?
// C(Args&&... params, double) : T(std::forward<Args>(params)...) { std::cout << "This is double\n"; }
};

int main() {
C<A> p(1.0, 1);
C<A> r(1.0, 1u);
C<B> q(1.0, 'c');

// Which would work as following
// C<A> p(1, 1.0);
// C<A> r(1u, 1.0);
// C<B> q('c', 1.0);
return 0;
}

我的问题是:

  • 此代码是否正确?这是我第一次尝试在构造函数中使用可变参数模板,所以如果我遗漏了什么,我很想听听你的意见。
  • 我更愿意将子类 C 的参数留在最后,但据我所知这是不可能的,因为在构造函数中不允许指定模板参数,在这种情况下,可变参数会吞掉所有参数,不为实际的 child 留下任何参数。有没有办法做到这一点?

最佳答案

14.8.2.1 从函数调用推导模板参数

1 [...] For a function parameter pack that does not occur at the end of the parameter-declaration-list, the type of the parameter pack is a non-deduced context.

5 [...] [ Note: If a template-parameter is not used in any of the function parameters of a function template, or is used only in a non-deduced context, its correspondingtemplate-argument cannot be deduced from a function call and the template-argument must be explicitly specified. — end note ]

14.8.2.5 从类型推导模板参数

If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

因此,如您所见,Args...应该明确指定,但由于这对于构造函数来说是不可能的,所以这不会起作用。

什么可以工作是将所有用于基构造函数的参数打包在一个元组(live example)中,然后是派生类的其余参数:

template <typename T>
class C : public T
{
template<typename... A, size_t... I>
C(std::tuple<A...>&& a, sizes<I...>, double x) :
T(std::get<I>(std::move(a))...) { std::cout << "This is double\n"; }

public:
template<typename... A>
C(std::tuple<A...>&& a, double x) :
C(std::move(a), idx<sizeof...(A)>{}, x) { }
};

用作

int main() {
C<A> p(std::forward_as_tuple(1), 1.0);
C<A> r(std::forward_as_tuple(1u, 2.), 1.0);
C<B> q(std::forward_as_tuple('c', 0), 1.0);
}

我让你的例子更丰富的地方:

struct A {
A(int) { std::cout << "This is int\n"; }
A(unsigned, double) { std::cout << "This is unsigned, double\n"; }
};

struct B {
B(char, int) { std::cout << "This is char, int\n"; }
};

和剩余的样板

template<size_t... I> struct sizes { using type = sizes<I...>; };

template<size_t N, size_t K = 0, size_t... I>
struct idx_t : idx_t<N, K+1, I..., K> {};

template<size_t N, size_t... I>
struct idx_t<N, N, I...> : sizes<I...> {};

template<size_t N>
using idx = typename idx_t<N>::type;

直到std::integer_sequence可用。

如果找到std::forward_as_tuple名称太长,不难定义您自己的名称,例如pack :

template<typename... A>
constexpr std::tuple<A&&...>
pack(A&&... a) { return std::tuple<A&&...>{std::forward<A>(a)...}; }

即便如此,语法

C<B> q(pack('c', 0), 1.0);

确实增加了一点开销,但我发现最终用户很自然地知道发生了什么。扁平化为 C<B> q('c', 0, 1.0);如果将更多参数添加到派生类构造函数,不仅几乎不可能实现,而且对用户来说也是模棱两可和令人困惑的。真正酷的是这样的语法

C<B> q('c', 0; 1.0);

(也例如在函数中分隔输入/输出参数),但我没有在任何语言中看到过这一点,只在教科书中看到过。

关于c++ - 构建父级的可变参数模板构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23550135/

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