【问题标题】:initializer list and conversion to everything, construction order初始化列表和转换为一切,构造顺序
【发布时间】:2016-01-13 08:26:59
【问题描述】:

情况如下:

#include <utility>
#include <typeinfo>
#include <iostream>

struct C1
{
    char const* str; 
    template <typename T> operator T() const { std::cout << "Convert to " << typeid(T).name() << "\n"; return {}; }
};

struct C2
{
    C2(C1 const&) { std::cout << "C2(C1)\n"; }
    C2(std::initializer_list<std::pair<char const*, int>>) { std::cout << "C2(list)\n"; }
};

int main()
{
    C1 c1{};
    C2 c2{c1};
}

输出表明正在调用C2(list)

我希望为 C1 参数调用 C2(C1),但我需要 std::initializer 列表的参数保持可推导和可转换,并且我不能用可变参数模板版本替换它。我只是想控制构造顺序,但是这里 //2 甚至不是模板。假设类型 std::pair 可以在正常情况下反序列化。可以使用C++14

【问题讨论】:

  • 问题是{} 调用initializer_list 构造函数,它优先于通常的构造函数。称它为C2 c2(c1) 有什么问题?或者您希望在您的 API 中使用这种特定语法?
  • 我猜你应该将operator T 替换为 C1 可以转换为的实际类型。或者更好的是,使用命名函数而不是转换运算符,并在需要时实际调用该函数。
  • @romeric 这不仅仅是我的界面。如果我在内部使用它,我会这样做
  • @M.M 它确实通过增强反序列化反序列化对象,这是为了反序列化用户想要的任何类型的对象,我不能用具体类型替换它
  • @M.M.我在努力。在我的情况下,不可能使用命名函数。 C1 被设计为代理类,并由某些容器的迭代器返回。 C2 是不可复制的、不可移动的和不可分配的 new 类。 c2 = c1 必须调用 c2 的构造函数。无法调用转换 c2 = c1.asT(),因为 C2 的移动和复制构造函数已被删除。 initializer_list 真的让所有 API 都崩溃了

标签: c++ type-conversion c++14 initializer-list


【解决方案1】:

下面选择C2(C1)这两种风格,{...}({...})(在严格的C++11标准中,对于({...})风格,它会选择第二个,因为C1是一个聚合和适用的特殊规则。最近的标准不再是这种情况。)。这是通过使第二个构造函数不再是初始化构造函数来实现的。

template<typename T>
struct id { typedef T type; };

struct C1 {
    char const* str; 
    template <typename T> operator T() const 
    { std::cout << "Convert to " << typeid(T).name() << "\n"; return {}; }
};

struct C2 {
    C2(C1);
    template<typename T = std::initializer_list<std::pair<char const*, int>>>
    C2(typename id<T>::type);
};

int main() {
    C1 c1{};
    C2 c2{c1};
    C2 c21({c1});
}

【讨论】:

  • 很好。仅与初始化列表一起使用时,我需要两次放置大括号,例如 C2 c22{{ {"bbb", 3}, {"aaa", 3} }}; 而不仅仅是 C2 c22{ {"bbb", 3}, {"aaa", 3} };
【解决方案2】:

您可以使用 explicit 关键字来防止构造函数进行隐式类型转换:

    explicit C2(std::initializer_list<std::pair<char const*, int>>) {} // 2

见:What does the explicit keyword mean in C++?

【讨论】:

  • 是的。我可以,但我需要带有可转换参数的 std::pair 才能保持可转换,如果我使用显式关键字,我将失去转换
  • 您将需要添加更多构造函数来执行允许的转换(例如,将 std::pair 作为参数)。
  • 会有十几个构造函数 :)
  • 我认为这意味着您并不真正想要 C1::operator T() 。也许可以考虑像 T& C1::asT(), const T& asT() const 这样的显式转换方法?
  • 在这种情况下,即使使用显式关键字,它也会尝试转换 c1。并且对于带有 std::list 的两个版本的构造函数,两者都是显式的,将是一个模棱两可的重载。这似乎不自然,但事实就是如此。我试过了。
猜你喜欢
  • 2012-03-19
  • 1970-01-01
  • 2010-11-17
  • 1970-01-01
  • 2023-03-12
  • 2011-05-01
  • 2021-06-12
相关资源
最近更新 更多