如何避免在std :: pair中"隐式"调用单参数构造函数

Dmi*_*kov 17 c++ stl

最初的问题是如何以std::map<std::wstring, std::wstring> >安全的方式使用,因为相同类型的密钥和值极易出错.所以我决定为这个值创建一个简单的包装器:

    struct ComponentName
    {
      std::wstring name;

      // I want to prohibit any implicit string-ComponentName conversions!!!
      explicit ComponentName(const std::wstring& _name) : name(_name)
      {
      }

      bool operator<(const ComponentName& item_to_compare) const
      {
        return name < item_to_compare.name;
      }
    };

    typedef std::map<std::wstring, ComponentName> component_names_map;
Run Code Online (Sandbox Code Playgroud)

但是以下代码运行良好!

component_names_map component_names;
// Are you sure that ComponentName's constructor cannot be called implicitly? ;)
component_names_map::value_type a_pair = std::make_pair(L"Foo", L"Bar");
Run Code Online (Sandbox Code Playgroud)

它的工作原理是因为std::pair<std::wstring, ComponentName>复制构造函数显式使用ComponentName的字符串构造函数来分配std::pair<std::wstring, std::wstring>实例.这是绝对合法的操作.但它看起来像是ComponentName构造函数的"隐式"调用.

所以我知道问题的原因,但我怎样才能避免这种'隐含' wstring-ComponentName转换呢?最简单的方法是不声明字符串构造函数,但它使ComponentName初始化不方便.

Fle*_*exo 10

我认为您可以通过std::pair为您的类型添加部分特化来合法地执行此操作:

namespace std {
    template <typename T>
    struct pair<T,ComponentName> {
       typedef T first_type;
       typedef ComponentName second_type;

       T first;
       ComponentName second;
       // The rest of the pair members:
       // ....
       // Any trick you like to make it fail with assignment/construction from 
       // pair<std::wstring, std::wstring>
    };
}
Run Code Online (Sandbox Code Playgroud)

理由:

§17.6.4.2.1规定了std命名空间中的特化的基本规则:

"只有当声明取决于用户定义的类型且专业化符合原始模板的标准库要求且未明确禁止时,程序才可以将任何标准库模板的模板专门化添加到命名空间std"

如果你在第20.3节的范围内填写了课程的其他部分,我看不出有任何明确的禁止来排除这个特殊情况.


另类,可能是合法的方法:

专业化std::is_constructible<ComponentName, std::wstring>value是假的.对于std::pair不同类型的s,这被列为赋值运算符和复制构造函数的要求.我看不到任何禁令从这个快速扫描下去,但我找不到任何说实现是需要检查的要求.