正确编写基于范围的构造函数

时间:2017-04-01 04:42:18

标签: c++ class c++11 c++14

我有一个问题要为一个类编写一个基于范围的构造函数,但找不到合适的措辞来搜索google上的帮助。

假设我正在写一个简单的类,比如vector,它涉及一个基于范围的构造函数,它将范围中的元素插入到当前容器中:

// foo.h
#ifndef FOO_H
#define FOO_H

#include <iostream>

class Foo {
public:
    Foo() {
        std::cout << "Default constructor called" << std::endl;
    }
    template<class InputIterator> Foo(InputIterator first, InputIterator last) {
        std::cout << "Range based constructor called" << std::endl;
    }

    Foo(size_t n, int val) {
        std::cout << "size_t, int constructor called" << std::endl;
    }

};

#endif // FOO_H

并拥有一个cpp文件

#include <iostream>
#include <vector>
#include "foo.h"

using std::cout;    using std::endl;

int main() {
    std::vector<int> v(10, 5);
    Foo f_default;
    Foo f_range(v.begin(), v.end());   
    Foo f_int(314, 159);       // want this to call size_t, int ctctr
    return 0;
}

在main的第三行,我们创建了一个Foo f_int(314, 159),直觉上我们想要调用size_t,int构造函数。但是它匹配范围的通用模板构造函数。有没有办法在C ++中解决这样的问题?我觉得我错误地处理了基于编写范围的构造函数。

我可以想象你可以使用模板专业化,但实际上并没有看到如何。

可能发生这种情况的一个例子是,如果我们正在编写一个向量类,其中有一个基于size_t和默认值的构造函数(将在类上进行模板处理,但我在这里进行了简化)和另一个基于迭代器的构造函数的范围内。

1 个答案:

答案 0 :(得分:4)

构造函数模板在第三种情况下更匹配,因为您传递了两个int参数,而Foo(size_t n, int val)需要进行intsize_t转换第一个论点。如果您将代码修改为

Foo f_int(static_cast<size_t>(314), 159);

调用所需的构造函数。但是,当然,这不是一个好的解决方案,因为它很容易意外地调用错误的构造函数。相反,您可以使用SFINAE从重载决策集中删除构造函数模板,方法是确保参数类型为迭代器。

template<class InputIterator,
         class = std::enable_if_t<
                    std::is_base_of<
                        std::input_iterator_tag,
                        typename std::iterator_traits<InputIterator>::iterator_category
                    >{}
                >
        >
Foo(InputIterator first, InputIterator last) {
    std::cout << "Range based constructor called" << std::endl;
}

如果查看表here,您可以读取的所有迭代器都是 InputIterators 或派生类型。所以上面的代码检查传递给构造函数的InputIterator类型是否属于该类型或从该类型派生的东西。