Search code examples
c++c++11visual-c++constructormsvc12

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?


Solution

  • 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