I have a template \'Foo\', which owns a T, and I\'d like it to have a variadic constructor that forwards its arguments to T\'s constructor:
template
You can use some ugly SFINAE with std::enable_if
, but I'm not sure it is better than your initial solution (in fact, I'm pretty sure it's worse!):
#include <memory>
#include <type_traits>
// helper that was not included in C++11
template<bool B, typename T = void> using disable_if = std::enable_if<!B, T>;
template<typename T>
struct Foo {
Foo() = default;
Foo(const Foo &) = default;
template<typename Arg, typename ...Args, typename = typename
disable_if<
sizeof...(Args) == 0 &&
std::is_same<typename
std::remove_reference<Arg>::type,
Foo
>::value
>::type
>
Foo(Arg&& arg, Args&&... args)
: t(std::forward<Arg>(arg), std::forward<Args>(args)...) {}
T t;
};
int main(int argc, char* argv[]) {
Foo<std::shared_ptr<int>> x(new int(42));
decltype(x) copy_of_x(x);
decltype(x) copy_of_temp(Foo<std::shared_ptr<int>>(new int));
return 0;
}
The best approach is to not do what you're doing.
That said, a simple fix is to let the variadic constructor forward up to a base class constructor, with some special first argument.
E.g. the following compiles with MinGW g++ 4.7.1:
#include <iostream> // std::wcout, std::endl
#include <memory> // std::shared_ptr
#include <stdlib.h> // EXIT_SUCCESS
#include <tuple>
#include <utility> // std::forward
void say( char const* const s ) { std::wcout << s << std::endl; }
template<typename T>
struct Foo;
namespace detail {
template<typename T>
struct Foo_Base
{
enum Variadic { variadic };
Foo_Base()
: t()
{ say( "default-init" ); }
Foo_Base( Foo_Base const& other )
: t( other.t )
{ say( "copy-init" ); }
template<typename ...Args>
Foo_Base( Variadic, Args&&... args )
: t( std::forward<Args>(args)... )
{ say( "variadic-init" ); }
T t;
};
template<typename T>
struct Foo_ConstructorDispatch
: public Foo_Base<T>
{
Foo_ConstructorDispatch()
: Foo_Base<T>()
{}
template<typename ...Args>
Foo_ConstructorDispatch( std::tuple<Foo<T>&>*, Args&&... args )
: Foo_Base<T>( args... )
{}
template<typename ...Args>
Foo_ConstructorDispatch( std::tuple<Foo<T> const&>*, Args&&... args )
: Foo_Base<T>( args... )
{}
template<typename ...Args>
Foo_ConstructorDispatch( void*, Args&&... args)
: Foo_Base<T>( Foo_Base<T>::variadic, std::forward<Args>(args)... )
{}
};
} // namespace detail
template<typename T>
struct Foo
: public detail::Foo_ConstructorDispatch<T>
{
template<typename ...Args>
Foo( Args&&... args)
: detail::Foo_ConstructorDispatch<T>(
(std::tuple<Args...>*)0,
std::forward<Args>(args)...
)
{}
};
int main()
{
Foo<std::shared_ptr<int>> x( new int( 42 ) );
decltype(x) copy_of_x( x );
}
If not, are there any adverse consequences of defining this non-const argument copy constructor?
I am going to ignore the "If not", since there are other approaches. But there is an adverse consequence of your approach. The following still uses the template constructor
Foo<X> g();
Foo<X> f(g());
Because g()
is an rvalue, the template is a better match because it deduces the parameter to an rvalue reference.
Disable the constructor when the argument type is the same type as or derived from this:
template<typename ThisType, typename ... Args>
struct is_this_or_derived : public std::false_type {};
template<typename ThisType, typename T>
struct is_this_or_derived<ThisType, T>
: public std::is_base_of<std::decay_t<ThisType>, std::decay_t<T> >::type {};
template<typename ThisType, typename ... Args>
using disable_for_this_and_derived
= std::enable_if_t<!is_this_or_derived<ThisType, Args ...>::value>;
Use it as
template<typename ...Args
, typename = disable_for_this_and_derived<Foo, Args ...> >
//^^^^
//this needs to be adjusted for each class
Foo(Args&&... args) : t(std::forward<Args>(args)...) {}