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

时间:2011-11-15 11:20:04

标签: 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;

但以下代码效果很好!

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初始化不方便。

3 个答案:

答案 0 :(得分: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>
    };
}

理由:

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

  

“程序可以为任何标准库添加模板专业化   模板到命名空间std只有声明依赖于a   用户定义的类型和专业化符合标准库   原始模板的要求并未明确说明   禁止“

如果您在第20.3节的范围内填写了本课程的其余部分,我看不出任何明确的禁止将这个特定情况排除在外。


替代方案,可能是法律方法:

专门化std::is_constructible<ComponentName, std::wstring>value为假。这被列为不同类型的std::pair的赋值运算符和复制构造函数的要求。我也看不到任何快速扫描禁止的禁令,但是我找不到任何说 required 的实现来检查要求。

答案 1 :(得分:4)

问题(在C ++ 03中)是大多数标准库实现并不是真正的标准符合。特别是,该标准规定当std::pair<T,U>由不同的std::pair<V,W>构造时,成员是通过隐式转换构造的。问题是在模板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 ) {} 
};

我实际上写了一篇关于这个特定案例here的博客文章,为此我试图提供适当的转换构造函数here,但解决方案不符合标准(即它有与标准所要求的签名不同。

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

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

要求:is_constructible :: value为true且is_constructible :: value为true。

     

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

     

备注:此构造函数不应参与重载决策,除非U可隐式转换为first_type且V可隐式转换为second_type。

对于template<class U, class V> pair(const pair<U, V>& p);的等效项,p9-11中存在等效限制(如果类型不是可移动

答案 2 :(得分:3)

简单:

enum FromString { fromString };

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