带有复制构造函数的基于继承的元组

问题描述 投票:0回答:1

我正在创建一个基于继承的元组,类似于 https://stackoverflow.com/a/52208842/1284735 中的完成方式,但使用构造函数除外。

我认为我的常规构造函数似乎工作正常,但是当我尝试使用复制构造函数时,编译器表示我没有向该函数提供任何参数。我错过了什么?

tuple.cpp:41:5:注意:候选构造函数不可行:需要单个参数“t”,但未提供任何参数 TupleImpl(TupleImpl& t):

template<size_t Idx, typename T>
struct TupleLeaf {
    T val;
    TupleLeaf() = default;
    // I think should also have proper constructors but left like this for simplicity
    TupleLeaf(T t): val(t) {} 
};

template<size_t Idx, typename... Args>
struct TupleImpl;

template<size_t Idx, typename T, typename... Args>
struct TupleImpl<Idx, T, Args...>: TupleLeaf<Idx, T>, TupleImpl<Idx + 1, Args...> {
    template<typename F, typename... Rest>
    TupleImpl(F&& val, Rest&&... args): TupleLeaf<Idx, T>(std::forward<F>(val)), TupleImpl<Idx + 1, Args...>(std::forward<Rest>(args)...) {}

    TupleImpl(TupleImpl& t): 
        TupleLeaf<Idx, T>(static_cast<TupleLeaf<Idx, T>>(t).val),
        TupleImpl<Idx + 1, Args...>(t) {}
};

template<size_t Idx>
struct TupleImpl<Idx> {
    TupleImpl() = default;
    TupleImpl(TupleImpl<Idx> &t) {}
};

template<typename... Args>
using Tuple = TupleImpl<0, Args...>;

int main() {
    Tuple<int, char, string> tup{1, 'a', "5"}; // Works okay
    Tuple<int, char, string> x = tup; // Fails here
}
c++ templates tuples
1个回答
0
投票

Tuple<int, char, std::string>
TupleImpl<0, int, char, std::string>
的别名,推导后有两个构造函数:

  1. TupleImpl(TupleImpl&)
    ,它是第一个构造函数的特化,其中
    F
    等于
    TupleImpl&
    并且
    Rest
    为空。
  2. 来自第二个构造函数的
  3. TupleImpl(TupleImpl&)

选择构造函数 #2 是因为非模板函数优于模板特化。 (就其价值而言,这不是典型的复制构造函数签名。)

此构造函数调用其基

TupleImpl<1, char, std::string>
的构造函数,并引用相同的参数(具有派生类的类型)。

推导后,又多了两个构造函数:

  1. TupleImpl(Tuple&)
    ,它是第一个构造函数的特化,其中
    F
    等于
    Tuple&
    并且
    Rest
    为空。
  2. TupleImpl(TupleImpl&)

由于参数的类型为

Tuple
,在这种情况下源自
TupleImpl
,因此第一个是更好的匹配。

然后,此构造函数使用转发的

Tuple<2, std::string>

 参数(这是一个空参数包)调用其基 
Rest
 的构造函数。它尝试默认构造
Tuple<2, std::string>
,但它没有默认构造函数。

这就是错误的含义:

Tuple<2, std::string>

的两个构造函数都需要一个参数,而我们传递零。

您可以通过使用

static_cast

 执行派生到基数转换来解决此问题。然而,模板构造函数仍然太贪婪了。即使您使用 
static_cast
,模板构造函数也需要受到约束,以免接管 
TupleImpl(const TupleImpl&)
 签名(或者相反,如果您使用更典型的复制构造函数签名)。您可以通过约束模板构造函数来解决这两个问题:

template<typename F, typename... Rest> requires (!std::derived_from<std::decay_t<F>, TupleImpl>) TupleImpl(F&& val, Rest&&... args): TupleLeaf<Idx, T>(std::forward<F>(val)), TupleImpl<Idx + 1, Args...>(std::forward<Rest>(args)...) {}

https://godbolt.org/z/5bxMbM5Yc

© www.soinside.com 2019 - 2024. All rights reserved.