Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't assign string literal to boxed std::string vector

This is a simplified version of my type system:

#include <string>
#include <vector>

template<typename T>
class Box {
public:
    Box(const T& value) : _value(value) {};
private:
    T _value;
    /* ... */
};

typedef Box<int> Int;
typedef Box<double> Double;
typedef Box<std::string> String;

int main(int argc, char* argv[]) {
    String a("abc");
    std::vector<String> b = { std::string("abc"), std::string("def") };

    // error C2664: 'Box<std::string>::Box(const Box<std::string> &)' : cannot convert argument 1 from 'const char' to 'const std::string &'
    std::vector<String> c = { "abc", "def" };
}

While a and b compile, c does not and the reason seems to be that I try to initialize from const char. This raises two questions:

  1. Why is b possible but not c? Is it because of the nested template in std::vector<Box<std::string> >?

  2. Can I make c work without destroying the general boxing mechanism (cf. typedef Box<double> Double?

like image 269
PhilLab Avatar asked May 22 '17 09:05

PhilLab


People also ask

Can a vector store strings C++?

Vector, being one such container, stores the array elements in a dynamic manner. Thus, C++ Vectors can be used to create a string array and manipulate the same easily. The vector. push_back(element) method is used to add elements to the vector string array.

Is vector character same as string?

However, a string is not exactly the same as a vector <char> . For example, a string has push_back (you could have s. push_back(ch); to add a char to a string) but it does not have a pop_back .


1 Answers

c currently need 2 implicit user conversions (const char [N] -> std::string -> String) whereas only one is allowed.

You may add template constructor to Box

template<typename T>
class Box {
public:
    Box() = default;
    Box(const Box&) = default;
    Box(Box&&) default;
    ~Box() = default;

    Box& operator=(const Box&) = default;
    Box& operator=(Box&&) = default;

    template <typename U0, typename ...Us,
              std::enable_if_t<std::is_constructible<T, U0, Us...>::value
                               && (!std::is_same<Box, std::decay_t<U0>>::value
                                  || sizeof...(Us) != 0)>* = nullptr>
    Box(U0&& u0, Us&&... us) : _value(std::forward<U0>(u0), std::forward<Us>(us)...) {}
private:
    T _value;
    /* ... */
};

Demo Demo2

like image 50
Jarod42 Avatar answered Oct 14 '22 17:10

Jarod42