13

这是我的类型系统的简化版本:

#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" };
}

虽然编译,a但没有,原因似乎是我尝试从. 这提出了两个问题:bcconst char

  1. 为什么b可能但不可能c?是因为嵌套的模板std::vector<Box<std::string> >吗?

  2. 我可以c在不破坏一般拳击机制的情况下工作(参见typedef Box<double> Double?

4

3 回答 3

18

c当前需要 2 次隐式用户转换 ( const char [N]-> std::string-> String),而只允许进行一次。

您可以将模板构造函数添加到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;
    /* ... */
};

演示 演示 2

于 2017-05-22T09:10:07.953 回答
3

仅在主要功能部分查看您的源代码:

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" };
}

您的第一行代码:

String a("abc");

正在使用此类模板采用 a的typedef版本,并且由于此模板版本需要 a它正在使用构造函数从 a构造 a ,这没关系。Box<std::string>const T&std::stringstd::string'sstd::stringconst char[3]

您的下一行代码:

std::vector<String> b = { std::string("abc"), std::string("def") };

std::vector<T>同上的。所以这也很有效,因为您正在vector<T>使用有效std::string对象进行初始化。

在您的最后一行代码中:

std::vector<String> c = { "abc", "def" };

您在此处声明where是一个c版本,但是您没有初始化with类型。您正在尝试使用对象或字符串文字对其进行初始化。vector<T>TtypedefBox<std::string>std::vector<T>Box<std::string>const char[3]

您可以尝试为第三行执行此操作:我没有尝试编译它,但我认为它应该可以工作。

std::vector<String> c = { String("abc"), String("def") };

编辑——我的意思是使用构造函数String而不是std::string进行适当的编辑。

于 2017-05-22T09:18:29.830 回答
2

您可以使用创建相应类型的辅助函数:

template <typename T,typename R>
Box<T> make_boxed(const R& value){
    return Box<T>(value);
}

T必须指定, 另一方面您可以将auto其用于返回的类型,这似乎是额外的复杂性。完整示例:

#include <string>

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

typedef Box<std::string> String;

int main(int argc, char* argv[]) {
    auto a = make_boxed<std::string>("asd");
}
于 2017-05-22T09:13:36.477 回答