如何在 C++ 中使用通用引用参数编写模板类的构造函数

Lon*_*ong 7 c++ c++17

我有一个带有两个类型参数的 C++ 模板类 MyClass:

template <typename D, typename S>
struct MyClass {
private:
    D d_;
    S s_;
};
Run Code Online (Sandbox Code Playgroud)

我想添加一个以 D 和 S 引用作为参数的构造函数,这样它就可以推断出 D 和 S:

template <typename D, typename S>
struct MyClass {
    // can bind to lvalues
    constexpr MyClass(const D &d, const S &s): d_(d), s_(s) {}

    // can bind to rvalues
    constexpr MyClass(D &&d, S &&s): d_(std::move(d)), s_(std::move(s)) {}

private:
    D d_;
    S s_;
};
Run Code Online (Sandbox Code Playgroud)

效果很好。我可以从左值或右值创建对象:

auto c1 = MyClass(1, std::string("Hello"));    // rvalues

auto idx = 2;
auto msg = std::string("World");
auto c2 = MyClass(idx, msg);    // lvalues
Run Code Online (Sandbox Code Playgroud)

但我只想要一个使用通用引用的构造函数,而不是两个分别处理左值和右值的构造函数。大概是这样的:

template <typename D, typename S>
struct MyClass {
    constexpr MyClass(D &&d, S &&s): d_(std::forward<D>(d)), s_(std::forward<S>(s)) {}
private:
    D d_;
    S s_;
};
Run Code Online (Sandbox Code Playgroud)

它不适用于语句“auto w2 = MyClass(i, j);”。编译器抱怨

error: no viable constructor or deduction guide for deduction of template arguments of 'MyClass'
auto c2 = MyClass(idx, msg);    // lvalues
          ^
note: candidate function [with D = int, S = std::__1::basic_string<char>] not viable: no known conversion from 'int' to 'int &&' for
      1st argument
    constexpr MyClass(D &&d, S &&s): d_(std::forward<D>(d)), s_(std::forward<S>(s)) {}
Run Code Online (Sandbox Code Playgroud)

命令行:c++ -std=c++17 test.cpp,编译为Apple clang version 12.0.0 (clang-1200.0.32.6)

看来构造函数只能接受右值。这不是通用参考。所以我的问题是我可以实现通用引用样式构造函数吗?(我还想将 MyClass 保留为具有两个类型参数 D 和 S 的模板类。)

StP*_*ere 9

在此代码片段中,ds不是通用引用 - 它们是右值引用(因为 D 和 S 已经通过类实例化在类中已知(推导) )。

 template <typename D, typename S>
    struct MyClass {
        constexpr MyClass(D &&d, S &&s): d_(std::forward<D>(d)), s_(std::forward<S>(s)) {}
    private:
        D d_;
        S s_;
    };
Run Code Online (Sandbox Code Playgroud)

根据Scott Meyers 的说法:类型推导需要涉及类型变量D&& and S&&并且仅以这种形式!),因为这些变量是通用引用,因此您需要额外的模板参数(因此类型是通过类方法推导的(=在本例中是 ctor)实例化):

template <typename D, typename S>
    struct MyClass {
        template <typename DD, typename SS>
        constexpr MyClass(DD &&d, SS &&s): d_(std::forward<DD>(d)), s_(std::forward<SS>(s)) {}
    private:
        D d_;
        S s_;
    };
Run Code Online (Sandbox Code Playgroud)

通过构造函数调用启用类模板类型推导可以通过类模板推导指南来完成,例如:

template <typename DD, typename SS> MyClass(DD &&d, SS &&s) -> MyClass<DD, SS>
Run Code Online (Sandbox Code Playgroud)