Acabei de escrever o seguinte código simples, mas ele não compila:
#include <iostream>
#include <string>
class Obj{
public:
std::string name = "Name";
std::string l_name = "LastName";
template<typename P>
Obj(P&& param): name{std::forward<P>(param)} { }
friend std::ostream& operator<<(std::ostream& os, const Obj& obj);
};
std::ostream& operator<<(std::ostream& os, const Obj& obj) {
os << obj.name << ":" << obj.l_name;
return os;
}
void print() {
std::cout << "}";
}
template<typename T, typename ...Args>
void print(T param, Args... args) {
std::size_t count = sizeof...(args);
std::cout << param;
if ( count != 0 ) {
std::cout << ",";
}
print(args...);
}
template<typename... Args>
void run(Args... args) {
std::cout << "{";
print(args...);
}
int main() {
Obj obj{"obj"};
run("1", "2", 1.3, std::string{"Some Message"}, obj);
return 0;
}
Acabei de usar um pacote de parâmetros simples e um exemplo de encaminhamento perfeito, mas deu o seguinte erro:
main.cpp: In instantiation of ‘Obj::Obj(P&&) [with P = Obj&]’:
main.cpp:49:8: required from here
main.cpp:12:21: error: no matching function for call to ‘std::__cxx11::basic_string::basic_string()’
12 | Obj(P&& param): name{std::forward<P>(param)} {
...
Se eu não usar o parâmetro obj na função run, o exemplo funcionará conforme o esperado.
Infelizmente
é muito ganancioso e pega
Obj(Obj&)
(para o que está errado).Você pode SFINAE esse construtor,
Demonstração
ou adicione sobrecargas extras
Demonstração
ou ainda mais simples, já que você não precisa de modelo aqui:
Demonstração
O problema é que o modelo do construtor
aceitaria um
Obj
lvalue.Obj
ainda possui um construtor de cópia e um construtor de movimento definidos implicitamente, mas eles possuem as assinaturasObj(const Obj&)
e,Obj(Obj&&)
respectivamente, portanto, perdem na resolução de sobrecarga quando um lvalue do tipoObj
é passado.Você pode ter um construtor de encaminhamento que use
P&&
, mas isso precisa ser devidamente restringido. Por exemplo,std::tuple
tem um construtor que aceita anyUTypes&&...
, mas é restrito a não aceitar astd::tuple
para que os construtores copiar/mover sempre ganhem.Em C++20 e superior, você poderia escrever
Em versões mais antigas, você poderia usar
std::enable_if
.Você também pode evitar totalmente o uso de modelos aqui criando um construtor:
Observe que você também pode usar um
std::string
valor by, mas isso geralmente é desaconselhado (consulte CppCoreGuidelines F.18: Para parâmetros “will-move-from”, passe byX&&
estd::move
o parâmetro ).