构造函数的优先级 C++

Pra*_*zek 6 c++ constructor iterator

我遇到了很奇怪的问题

对于这样的代码

    template <typename T>
    struct A{
        explicit A(unsigned int size = 0, const T &t = T())
        {
        }
        template <typename InputIterator>
        A(InputIterator first, InputIterator last) {
            for(;first != last ; ++first)
            {
                *first; //do something with iterator
            }
        }
    };
Run Code Online (Sandbox Code Playgroud)

例如,当我定义

        A<int> a(10,10);
Run Code Online (Sandbox Code Playgroud)

使用迭代器的第二个构造函数而不是第一个构造函数。那么当向量构造函数看起来很漂亮时,它们是如何工作的呢?

    explicit vector (size_type n, const value_type& val = value_type(),
             const allocator_type& alloc = allocator_type());

    template <class InputIterator>
     vector (InputIterator first, InputIterator last,
             const allocator_type& alloc = allocator_type());
Run Code Online (Sandbox Code Playgroud)

我可以制作向量 v(10,10) 而不会有任何麻烦。

PS我有这样的错误

      temp.cpp: In instantiation of ‘A<T>::A(InputIterator, InputIterator) [with = int; T = int]’:
    temp.cpp:17:15:   required from here
    temp.cpp:12:4: error: invalid type argument of unary ‘*’ (have ‘int’)
Run Code Online (Sandbox Code Playgroud)

AnT*_*AnT 4

编译器在 your 的情况下选择第二个构造函数的原因A很简单: your是有符号类型10的值,而是某种无符号整数类型。这意味着必须转换为无符号整数类型。这种转换的需要使得第一个构造函数失去了第二个构造函数的重载解析(这与 完全匹配)。您可以通过执行以下操作来解决此问题intsize_type10InputIterator = int

\n
A<int> a(10u, 10);\n
Run Code Online (Sandbox Code Playgroud)\n

这消除了转换的需要int -> unsigned,并使第一个构造函数通过“非模板优于模板”子句赢得重载决策。

\n

同时,它的工作方式不同的原因std::vector是语言规范对标准序列的构造函数给予了特殊对待。它只要求使用std::vector两个相同类型的整数作为参数的构造函数调用以某种方式“神奇地”解析为引用中的第一个构造函数(即大小和初始化构造函数)。每个具体实现如何实现这一点取决于实现。它可以重载所有整数类型的构造函数。它可以使用类似于 的功能enable_if。它甚至可以将其硬编码到编译器本身中。等等。

\n

例如,C++03 中是这样表述的

\n
\n

23.1.1 序列

\n

9对于本节和第 21 节中定义的每个序列:

\n

\xe2\x80\x94 构造函数

\n
\n
template <class InputIterator> \nX(InputIterator f, InputIterator l, const Allocator& a = Allocator()) \n
Run Code Online (Sandbox Code Playgroud)\n
\n

具有与以下相同的效果:

\n
\n
X(static_cast<typename X::size_type>(f),\n  static_cast<typename X::value_type>(l), a) \n
Run Code Online (Sandbox Code Playgroud)\n
\n

如果 InputIterator 是整型

\n
\n

C++11 通过从不同的角度来处理它,从而更进一步,尽管意图保持不变:它声明如果InputIterator不符合输入迭代器的条件,则应将模板构造函数排除在重载解析之外。

\n

因此,如果您希望类模板A具有相同的行为方式std::vector,则必须刻意以这种方式设计它。实际上,您可以查看您平台上的标准库实现,看看它们是如何实现的std::vector

\n

无论如何,一个低技术的暴力解决方案是为int参数添加一个专用的重载构造函数

\n
    explicit A(unsigned int size = 0, const T &t = T())\n    { ... }\n\n    explicit A(int size = 0, const T &t = T())\n    { ... }\n
Run Code Online (Sandbox Code Playgroud)\n

当然,这可能意味着您最终必须为所有整数类型添加重载。

\n

一个更好的解决方案(我在上面已经提到过)是通过使用或类似的基于 SFINAE 的技术来禁用整数参数的模板构造函数。enable_if例如

\n
    template <typename InputIterator>\n    A(InputIterator first, InputIterator last, \n      typename std::enable_if<!std::is_integral<InputIterator>::value>::type* = 0)\n
Run Code Online (Sandbox Code Playgroud)\n

您的编译器中是否有可用的 C++11 功能?

\n

  • @KonradRudolph,在 N3690 中它是 [sequence.reqmts]/14,_“如果使用不符合输入迭代器资格的类型 `InputIterator` 调用构造函数 [...],则构造函数不应参与重载决策."_ 和 15 _"实现确定类型不能是输入迭代器的程度未指定,但作为最小整型类型不应有资格作为输入迭代器。"_ (3认同)