请考虑以下代码:
#include <boost/range.hpp>
#include <boost/iterator/counting_iterator.hpp>
typedef boost::iterator_range<boost::counting_iterator<int>> int_range;
template <typename T>
class Ref {
T* p_;
public:
Ref(T* p) : p_(p) { }
/* possibly other implicit conversion constructors,
but no unconstrained template constructors that don't
use the explicit keyword... */
operator T*() const { return p_; }
operator const T*() const { return p_; }
};
struct Bar { };
class Foo {
public:
Foo(int a, char b) { /* ... */ }
Foo(int a, const Ref<Bar>& b) { /* ... */ }
Foo(int a, const int_range& r) { /* ... */ }
};
int main() {
Bar b;
Foo f(5, &b);
return 0;
}
此代码无法编译,因为Foo
构造函数的使用不明确,因为boost::iterator_range
显然具有模板化构造函数,该构造函数接受单个参数并且未声明为explicit
。假设更改Ref
的结构不是一个选项,我该如何解决这个问题?我提出了以下可能的解决方案,但它很难看并且不易维护,特别是如果Foo
有多个构造函数:
template<typename range_like>
Foo(
int a,
const range_like& r,
typename std::enable_if<
not std::is_convertible<range_like, Ref<Bar>>::value
and std::is_convertible<range_like, int_range>::value,
bool
>::type unused = false
) { /* ... */ }
或类似地
template<typename range_like>
Foo(
int a,
const range_like& r,
typename std::enable_if<
std::is_same<typename std::decay<range_like>::type, int_range>::value,
bool
>::type unused = false
) { /* ... */ }
的缺点是int_range
的所有其他隐式类型转换都被禁用,因此依赖于boost
的未指定特征(我的直觉告诉我,这可能是一个坏主意)。有一个更好的方法吗? (除了C ++ 14“概念 - 精简版”,这就是我想到的问题)。
答案 0 :(得分:7)
我认为这个程序是你问题的最小例子:
#include <iostream>
struct T {};
struct A {
A(T) {}
};
struct B {
B(T) {}
};
struct C {
C(A const&) { std::cout << "C(A)\n"; }
C(B const&) { std::cout << "C(B)\n"; }
};
int main() {
C c{T{}};
}
您有两种类型A
和B
都可以隐式转换为其他类型T
,另一种类型C
可以隐式转换为A
和{{1}但是,B
的隐式转换是模糊的。您希望消除歧义情况,以便T
可以使用转化序列C
从T
隐式转换,但您必须这样做而不更改T => A => C
和{的定义{1}}。
明显的解决方案 - 已在评论中提出 - 是为A
引入第三个转换构造函数:B
。你已经拒绝了这个解决方案,因为它不够通用,但没有说明“一般”问题是什么。
我猜想你想要解决的更普遍的问题是让C
明确地从任何类型C(T value) : C(A(value)) {}
隐式转换为可隐式转换为C
的{{1}}转化顺序U
。这可以通过向A
(Live code demo at Coliru)引入额外的模板构造函数来实现:
U => A => C
模板构造函数是C
的直接匹配,因此明确优先于需要转换的template <typename U, typename=typename std::enable_if<
!std::is_base_of<A,typename std::decay<U>::type>::value &&
std::is_convertible<U&&, A>::value>::type>
C(U&& u) : C(A{std::forward<U>(u)}) {}
和C(U)
构造函数。它仅限于接受C(A)
类型
C(B)
可转换为U
(原因很明显)U
not A
或对U
的引用或从A
派生的类型,以避免与{{1}的歧义在A
例如的情况下,构造函数和无限递归A
或C(const A&)
。值得注意的是,此解决方案不需要更改U
,A&
,A&&
,T
或A
的定义,因此它非常自包含