Je pense que tu as un problème si tu passes plusieurs paramètres dont au moins un est une référence. Si tu tiens à aller dans cette direction, utilise un type_traits custom dans le genre :
Code:
template <typename T, typename... Args>
struct is_copy_or_move_constructor_args: std::false_type {};
template <typename T>
struct is_copy_or_move_constructor_args<T, const T &>: std::true_type {};
template <typename T>
struct is_copy_or_move_constructor_args<T, T &>: std::true_type {};
template <typename T>
struct is_copy_or_move_constructor_args<T, T &&>: std::true_type {};
Ces types de références ne sont pas suffisant, dans cet article, l'auteur prend on compte le cas où on passe un objet de type dérivé au constructeur de copy dans la classe de base. Il faudrait peut-être aller jusqu'à considérer tout ce qui est convertible en référence ?
C'est très compliqué, je préfère contourner le problème avec des fonctions libres/méthodes statiques make_truc, ou des constructeurs à tags (qui peuvent être utilisés en interne par les fonctions make_truc, si tu les trouves moches).
Edit: J'ai retrouvé l'exemple dans le livre de Scott Meyers, c'est is_base_of + decay.
Code:
class Person {
public:
template<
typename T,
typename = std::enabled_if_t<
!std::is_base_of<Person, std::decay_t<T>>::value
&&
!std::is_integral<std::remove_reference_t<T>>::value
>
>
explicit Person(T&& n) // ctor for std::strings and
: name(std::forward<T>(n)) // args convertible to
{ … } // std::strings
explicit Person(int idx) // ctor for integral args
: name(nameFromIdx(idx))
{ … }
… // copy and move ctors, etc.
private:
std::striing name;
};