如何避免在 std::p air 中调用'implicit'单参数构造函数

How to avoid 'implicit' calling of a one-parameter constructor in std::pair

本文关键字:单参数 参数 构造函数 implicit 何避免 air 调用 std      更新时间:2023-10-16

最初的问题是如何以一种安全的方式处理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;

但是下面的代码工作得很好!

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

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

所以我知道问题的原因,但是我如何避免这种"隐式"wstring-ComponentName转换?最简单的方法是不声明字符串构造函数,但这会使ComponentName初始化变得不方便。

我认为你可以通过为你的类型添加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>
    };
}

的理由:

§17.6.4.2.1列出了std命名空间中专门化的基本规则:

"程序可以为任何标准库添加模板专门化模板仅在声明依赖于用户定义类型和专门化满足标准库对原始模板的要求并不明确禁止"

我看不出有任何明确的禁令可以排除这种特殊情况,只要你在§20.3的范围内填写了类的其余部分。


另一种可能合法的方法:

std::is_constructible<ComponentName, std::wstring>进行专门化,使value为假。对于不同类型的std::pair,这是赋值操作符和复制构造函数的要求。快速浏览一下,我也看不出有什么禁忌,但是我找不到任何东西说实现需要来检查需求。

问题(在c++ 03中)是大多数标准库实现并不真正符合标准。特别是,标准规定,当从不同的std::pair<V,W>构造std::pair<T,U>时,成员通过隐式转换构造。问题是,在模板化的pair构造函数的实现中,实际上很难(如果可能的话)限制这种转换,因此当前的实现对参数执行显式转换:

template <typename T, typename U>
struct pair {
    // ...
    template <typename V, typename W>
    pair( pair<V,W> const & p ) : first( p.first ), second( p.second ) {} 
};

我实际上在这里写了一篇关于这个特殊情况的博客文章,为了它的缘故,我试图在这里提供适当的转换构造函数,但是解决方案不符合标准(即它具有与标准要求的签名不同的签名)。

注意:在c++ 11(§20.3.2p12-14)中,这种隐式转换也是被禁止的(来自FDIS):

template<class U, class V> pair(pair<U, V>&& p);

要求:is_constructible::value为真且is_constructible::value为真

效果:构造函数首先用std::forward(p.first)初始化,然后用std::forward(p.second)初始化。

注意:除非U隐式转换为first_type, V隐式转换为second_type,否则此构造函数不参与重载解析。

在p9-11中对template<class U, class V> pair(const pair<U, V>& p);的等效值有等效的限制(如果类型不是movable)

Simple:

enum FromString { fromString };
ComponentName( FromString, std::wstring const& aName)
    : name( aName )
{}