1

I don't get why this is not working:

template <typename T>
struct TypeWrapper
{
    typedef T type;
};
template <>
struct TypeWrapper<char*>
{
    typedef std::string type;
};
template <>
struct TypeWrapper<const char*>
{
    typedef std::string type;
};
template <int N>
struct TypeWrapper<char[N]>
{
    typedef std::string type;
};
template <int N>
struct TypeWrapper<const char[N]>
{
    typedef std::string type;
};

class A
{
public:
    template< typename T > 
    A( const typename TypeWrapper<T>::type& t )
    {
        // do smthing
        std::cout << t << std::endl;
    }
};

int main( void )
{
    A a( 42 );

    return 0;
}

I compile with Visual Studio 2010 and I get the following error:

error C2664: 'A::A(const A &)' : cannot convert parameter 1 from 'int' to 'const A &'

If I change the constructor of A to this one it works:

A( const T& t )

But I'd like to handle char* types as std::strings and possibly other type adjustements, whithouth duplicating the constructor (defining a constructor specific to each type, this works)

foke
  • 1,339
  • 2
  • 12
  • 20
  • 2
    You can't deduce template arguments like this. It's the same as trying to deduce when you take a `std::vector`. – chris Sep 16 '13 at 18:58

1 Answers1

2

I believe the following is not correct syntactically

A( typename const TypeWrapper<T>::type& t )

It should be

A( const typename TypeWrapper<T>::type& t )

or

A( typename TypeWrapper<T>::type const& t )

Anyway, your example will not compile even if you fix that problem. VC++ is attempting to call the (compiler generated) copy constructor instead of the constructor you've defined because template argument deduction will always fail on your constructor. The reason for that is the standard defines referring to a nested type name like the one in your constructor argument (typename TypeWrapper<T>::type) is a non-deduced context.

This leaves you with no way of constructing A, since template arguments for constructors must be deduced; you can't explicitly specify them.


You should probably resort to overloading.

class A
{
public:
    template< typename T > 
    A( T const& t )
    {
        // do smthing
        std::cout << t << std::endl;
    }

    A( std::string const& s )
    {
       std::cout << "string" << std::endl;
    }

    A ( char const *s )
    {
       std::cout << "char *" << std::endl;
    }

    template<std::size_t N>
    A ( const char (&arr)[N] )
    {
       std::cout << "char array" << std::endl;
    }
};
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • I tried all 3 permutations, none is working indeed; and actually, naturally I typed the const typename ... one, what you saw in the questions was the result of my experiments :) As for the last part of your answer I'm not sure I get it ... – foke Sep 16 '13 at 19:08
  • @foke I'm not sure what 3 permutations you're talking about. And what part is not clear to you? – Praetorian Sep 16 '13 at 19:11
  • I wanted to avoid overloading because in the real world case the constructor is a tad less trivial – foke Sep 16 '13 at 19:19
  • I meant permuations of const, and the last part I meant your explanation of why it'll never work – foke Sep 16 '13 at 19:20
  • @foke then perhaps this sample (which is correct) isn't blunt enough (though I think it is): You don't specialize functions; you overload them. You specialize *types*; not functions. (member or otherwise). What you're asking for would be equivalent to `std::pair v(42,"foo")` somehow knowing that because `42` is an `int` and `"foo"` is a `const char*` magic ensues on the `std::pair` template type deduction *declaration*, which simply isn't how the language works. – WhozCraig Sep 16 '13 at 19:24
  • @foke It's difficult to suggest workarounds without more detail, but a few possibilities: 1. C++11 allows delegating constructors, so you could have a default constructor (maybe private) that implements common functionality, this is then called by overloaded constructors. VS2010 *does not* implement this. 2. Similar to 1, move all common functionality to a base class, and have `A` contain an instance of this class. The base class constructor will perform initialization before each constructor runs. 3. Move common functionality to an `initialize()` function that is called from each constructor. – Praetorian Sep 16 '13 at 19:25
  • @foke I'm not sure how to explain this any better, but in the expression `typename TypeWrapper::type` you're referring to a nested type name (the `::type` part) and the standard says `T` cannot be deduced in this case. As for why you can't explicitly specify template arguments for a constructor template, @WhozCraig explains it above. Also, refer to [this answer](http://stackoverflow.com/a/3960925/241631). – Praetorian Sep 16 '13 at 19:33
  • yes don't worry I was mostly interested in why this was not working, nothing more :) I'll accept your answer – foke Sep 16 '13 at 19:37