如何避免在 std::p air 中调用'implicit'单参数构造函数
How to avoid 'implicit' calling of a one-parameter constructor in std::pair
最初的问题是如何以一种安全的方式处理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 )
{}
- 模板参数推导失败,函数参数/参数不匹配
- 有人可以帮助我理解这些参数/参数吗?
- 为什么具有 2 个参数参数的构造函数接受复制对象作为 1 个参数参数?
- 当超出列时,clang格式强制每个参数/参数拥有自己的行?
- 常量和非常量函数作为模板参数参数
- 功能指针参数参数转换为const
- C 从参数参数包创建向量或列表
- C 中函数参数/参数的分辨率
- 操作员[]作为成员函数的正确模板参数/参数是什么?
- 为什么可以将其他类型变量用作C 中常量参考参数参数的参数
- 如何从Windows CMD运行可执行文件并将其传递给文件输入的参数参数和重定向
- 使用 {} 作为默认参数参数类型
- 将多个cmd.exe参数/参数传递给Shellexecute(ex)
- C 参数参数未更新
- 如何识别模板参数参数是否是模板内结构中另一个类的实例?C++
- 在不使用"new"的情况下实例化参数参数中的对象
- 由 pthread_create() 调用的函数的多个参数 - 参数是函数指针
- 模板模板参数参数名称用法
- 重新定义默认参数:参数 2
- 声明我的成员函数参数/参数