最初的问题是如何以安全的方式使用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初始化不方便。
答案 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 )
{}