no one special
no one special

Reputation: 1764

How to call the right constructor of a template type?

In the following code, how can I make the commented line working in the same way as the line just above of it?

I would like to make it a generic code, that call suitable constructor of a template Type.

#include <string>
#include <iostream>

template <typename Type>
struct Class
{
    Type data;
    Class(Type data) : data(data) { }
};

int main()
{
    Class<std::string> a = std::string("abc");
    // Class<std::string> b = "abc";
    std::cout << a.data << std::endl;
    return 0;
}

Upvotes: 21

Views: 715

Answers (4)

florestan
florestan

Reputation: 4655

If you are able to change your Class, you could add a templated converting constructor. Then you'd be able to compile the commented line as written in your example. Notehowever, that it is generally not recommended to use implicit conversions without decent reason as they may result in hard-to-spot bugs (cf the C++ Core Guidlines).

#include <string>
#include <iostream>

template <typename Type>
struct Class
{
    Type data;
    Class(Type data) : data(data) { }

    template<typename Other>
    Class(Other other_data) : data(other_data) {}
};


int main()
{
    Class<std::string> a = std::string("abc");
    Class<std::string> b = "abc";
    Class<std::string> c = a;

    std::cout << b.data << std::endl;
    return 0;
}

If you can use C++14, you could use the std::literals::string_literals::operator""s and remove the converting constructor. Then, your line would look like this:

using namespace std::literals;

Class<std::string> b = "abc"s;

Live code here.

Upvotes: 2

dranjohn
dranjohn

Reputation: 693

Use direct initialization:

Class<std::string> b("abc");

Upvotes: 14

Evg
Evg

Reputation: 26302

Class<std::string> b = "abc";

is copy-initialization. It doesn't work because it would involve two user-defined conversions:

  • from const char* to std::string,
  • from std::string to Class<std::string>.

But at most one is allowed.

When you write

Class<std::string> b("abc");
// or
Class<std::string> b{"abc"};

you use direct-initialization. It works because now only one user-defined conversion is used:

  • from const char* to std::string.

Upvotes: 14

JeJo
JeJo

Reputation: 32847

Use braced-init-list(or uniform-initiation) to initlize the instance of Class.

Class<std::string> a{ std::string("abc") };  // works
Class<std::string> b{ "abc" };               // also works

Upvotes: 17

Related Questions