如何实现构造函数,使其只接受使用 typeid 的输入迭代器?
How to implement a constructor so it only accepts input iterators using typeid?
我想为某个对象实现一个范围构造函数,但我想限制它只接受两个输入迭代器。
我尝试用 gcc 7.1.0 编译这段代码。
文件test.cpp
#include <vector>
#include <type_traits>
#include <typeinfo>
template <typename Iterator>
using traits = typename std::iterator_traits<Iterator>::iterator_category;
template <typename T>
class A{
private:
std::vector<T> v;
public:
template <typename InputIterator,
typename = std::enable_if_t<
typeid(traits<InputIterator>) ==
typeid(std::input_iterator_tag)>
>
A(InputIterator first, InputIterator last) : v(first, last) {}
};
int main(){
std::vector<double> v = {1, 2, 3, 4, 5};
A<double> a(v.begin(), v.end());
}
我用 g++ test.cpp -o test
得到这个编译错误:
test.cpp: In function ‘int main()’:
test.cpp:27:34: error: no matching function for call to ‘A<double>::A(std::vector<double>::iterator, std::vector<double>::iterator)’
A<double> a(v.begin(), v.end());
^
test.cpp:22:7: note: candidate: template<class InputIterator, class> A<T>::A(InputIterator, InputIterator)
A(InputIterator first, InputIterator last) : v(first, last) {}
^
test.cpp:22:7: note: template argument deduction/substitution failed:
test.cpp: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = ((const std::type_info*)(& _ZTISt26random_access_iterator_tag))->std::type_info::operator==(_ZTISt18input_iterator_tag); _Tp = void]’:
test.cpp:18:16: required from here
test.cpp:19:49: error: call to non-constexpr function ‘bool std::type_info::operator==(const std::type_info&) const’
typeid(traits<InputIterator>) ==
test.cpp:18:16: note: in template argument for type ‘bool’
typename = std::enable_if_t<
^~~~~~~~
test.cpp:10:7: note: candidate: A<double>::A(const A<double>&)
class A{
^
test.cpp:10:7: note: candidate expects 1 argument, 2 provided
test.cpp:10:7: note: candidate: A<double>::A(A<double>&&)
test.cpp:10:7: note: candidate expects 1 argument, 2 provided
我决定使用默认模板参数,因为它更适合构造函数。使用运算符 typeid()
是因为我发现在维护代码时它真的很容易阅读,但我无法以任何方式让它工作。
其他解决方案看起来很奇怪而且非常晦涩(例如强制 InputIterator 参数具有某些方法,例如 *it 或 ++it)。如果我无法做到这一点,我将不胜感激一个或多或少易于阅读的解决方案。
The use of the operator typeid()
is because I find it really easy to read when mantaining the code, but I can't get it to work in any way.
typeid
是一个主要用来在运行时查询类型信息的工具。虽然您可能会发现它更多 "readable",但它不是完成这项工作的正确工具,它会让所有其他 C++ 开发人员感到困惑。
Other solutions look very weird and are really obscure (like forcing the InputIterator parameter to have certain methods such as *it
or ++it
)
强烈建议您重新考虑一下。一个InputIterator
is a concept that describers valid operations on a type. The whole idea behind a concept is checking for operation validity - there is nothing more than InputIterator
than its requirements.
解决问题的正确且惯用的方法是创建一个 constexpr bool is_input_iterator<T>
variable/function 如果 T
匹配 InputIterator
returns true
的要求。您可以使用 detection idiom... 轻松实现它,而且它的可读性不比 std::enable_if_t<is_input_iterator<T>>
.
这里是一个简化的例子(我没有检查所有的要求):
template <typename T>
using supports_increment = decltype(++std::declval<T>());
template <typename T>
using supports_dereference = decltype(*std::declval<T>());
template <typename T>
constexpr bool is_input_iterator =
std::experimental::is_detected_v<supports_increment, T>
&& std::experimental::is_detected_v<supports_dereference, T>;
template <typename InputIterator,
typename = std::enable_if_t<is_input_iterator<InputIterator>>>
A(InputIterator first, InputIterator last) : v(first, last) {}
为了执行 SFINAE,您需要确保涉及表达式的计算发生在编译时。对于 typeid
以下适用:
When applied to an expression of polymorphic type, evaluation of a
typeid expression may involve runtime overhead (a virtual table
lookup), otherwise typeid expression is resolved at compile time.
因此,我认为 typeid
不是静态(编译时)多态性的好选择。
解决问题的一种方法是结合使用标记分派和委托构造函数,如下所示:
template <typename T>
class A{
std::vector<T> v;
template <typename InputIterator>
A(InputIterator first, InputIterator last, std::input_iterator_tag) : v(first, last) {}
public:
template<typename InputIterator> A(InputIterator first, InputIterator last)
: A(first, last, typename std::iterator_traits<InputIterator>::iterator_category()) {}
};
我想为某个对象实现一个范围构造函数,但我想限制它只接受两个输入迭代器。
我尝试用 gcc 7.1.0 编译这段代码。
文件test.cpp
#include <vector>
#include <type_traits>
#include <typeinfo>
template <typename Iterator>
using traits = typename std::iterator_traits<Iterator>::iterator_category;
template <typename T>
class A{
private:
std::vector<T> v;
public:
template <typename InputIterator,
typename = std::enable_if_t<
typeid(traits<InputIterator>) ==
typeid(std::input_iterator_tag)>
>
A(InputIterator first, InputIterator last) : v(first, last) {}
};
int main(){
std::vector<double> v = {1, 2, 3, 4, 5};
A<double> a(v.begin(), v.end());
}
我用 g++ test.cpp -o test
得到这个编译错误:
test.cpp: In function ‘int main()’:
test.cpp:27:34: error: no matching function for call to ‘A<double>::A(std::vector<double>::iterator, std::vector<double>::iterator)’
A<double> a(v.begin(), v.end());
^
test.cpp:22:7: note: candidate: template<class InputIterator, class> A<T>::A(InputIterator, InputIterator)
A(InputIterator first, InputIterator last) : v(first, last) {}
^
test.cpp:22:7: note: template argument deduction/substitution failed:
test.cpp: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = ((const std::type_info*)(& _ZTISt26random_access_iterator_tag))->std::type_info::operator==(_ZTISt18input_iterator_tag); _Tp = void]’:
test.cpp:18:16: required from here
test.cpp:19:49: error: call to non-constexpr function ‘bool std::type_info::operator==(const std::type_info&) const’
typeid(traits<InputIterator>) ==
test.cpp:18:16: note: in template argument for type ‘bool’
typename = std::enable_if_t<
^~~~~~~~
test.cpp:10:7: note: candidate: A<double>::A(const A<double>&)
class A{
^
test.cpp:10:7: note: candidate expects 1 argument, 2 provided
test.cpp:10:7: note: candidate: A<double>::A(A<double>&&)
test.cpp:10:7: note: candidate expects 1 argument, 2 provided
我决定使用默认模板参数,因为它更适合构造函数。使用运算符 typeid()
是因为我发现在维护代码时它真的很容易阅读,但我无法以任何方式让它工作。
其他解决方案看起来很奇怪而且非常晦涩(例如强制 InputIterator 参数具有某些方法,例如 *it 或 ++it)。如果我无法做到这一点,我将不胜感激一个或多或少易于阅读的解决方案。
The use of the operator
typeid()
is because I find it really easy to read when mantaining the code, but I can't get it to work in any way.
typeid
是一个主要用来在运行时查询类型信息的工具。虽然您可能会发现它更多 "readable",但它不是完成这项工作的正确工具,它会让所有其他 C++ 开发人员感到困惑。
Other solutions look very weird and are really obscure (like forcing the InputIterator parameter to have certain methods such as
*it
or++it
)
强烈建议您重新考虑一下。一个InputIterator
is a concept that describers valid operations on a type. The whole idea behind a concept is checking for operation validity - there is nothing more than InputIterator
than its requirements.
解决问题的正确且惯用的方法是创建一个 constexpr bool is_input_iterator<T>
variable/function 如果 T
匹配 InputIterator
returns true
的要求。您可以使用 detection idiom... 轻松实现它,而且它的可读性不比 std::enable_if_t<is_input_iterator<T>>
.
这里是一个简化的例子(我没有检查所有的要求):
template <typename T>
using supports_increment = decltype(++std::declval<T>());
template <typename T>
using supports_dereference = decltype(*std::declval<T>());
template <typename T>
constexpr bool is_input_iterator =
std::experimental::is_detected_v<supports_increment, T>
&& std::experimental::is_detected_v<supports_dereference, T>;
template <typename InputIterator,
typename = std::enable_if_t<is_input_iterator<InputIterator>>>
A(InputIterator first, InputIterator last) : v(first, last) {}
为了执行 SFINAE,您需要确保涉及表达式的计算发生在编译时。对于 typeid
以下适用:
When applied to an expression of polymorphic type, evaluation of a typeid expression may involve runtime overhead (a virtual table lookup), otherwise typeid expression is resolved at compile time.
因此,我认为 typeid
不是静态(编译时)多态性的好选择。
解决问题的一种方法是结合使用标记分派和委托构造函数,如下所示:
template <typename T>
class A{
std::vector<T> v;
template <typename InputIterator>
A(InputIterator first, InputIterator last, std::input_iterator_tag) : v(first, last) {}
public:
template<typename InputIterator> A(InputIterator first, InputIterator last)
: A(first, last, typename std::iterator_traits<InputIterator>::iterator_category()) {}
};