I have a C++ template class MyClass with two type parameters:
template <typename D, typename S>
struct MyClass {
private:
D d_;
S s_;
};
I want to add a constructor with D and S references as parameters, so it can get D and S inferred:
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_;
};
It works well. I can create objects from either lvalues or rvalues:
auto c1 = MyClass(1, std::string("Hello")); // rvalues
auto idx = 2;
auto msg = std::string("World");
auto c2 = MyClass(idx, msg); // lvalues
But I'd like just one constructor using universal references rather than two with each handles lvalues and rvalues. Probably like this:
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_;
};
It does not work for statement "auto w2 = MyClass(i, j);". The compiler complains
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)) {}
Command line: c++ -std=c++17 test.cpp, compiled with Apple clang version 12.0.0 (clang-1200.0.32.6)
It seems the constructor can only accept rvalues. It's not the universal references. So my question is can I achieve the universal reference style constructor? (Also I want to keep MyClass as a template class with the two type parameters D and S.)