以下代码无法编译:
#include <boost/variant.hpp>
class A {};
class B {};
class C {};
class D {};
using v1 = boost::variant<A, B>;
using v2 = boost::variant<C, D>;
int f(v1 const&) {
return 0;
}
int f(v2 const&) {
return 1;
}
int main() {
return f(A{});
}
Run Code Online (Sandbox Code Playgroud)
gcc和clang都抱怨:
test1.cpp: In function ‘int main()’:
test1.cpp:18:17: error: call of overloaded ‘f(A)’ is ambiguous
return f(A{});
^
test1.cpp:18:17: note: candidates are:
test1.cpp:11:5: note: int f(const v1&)
int f(v1 const&) {
^
test1.cpp:14:5: note: int f(const v2&)
int f(v2 const&) {
Run Code Online (Sandbox Code Playgroud)
鉴于无法v2从A实例构造对象,为什么编译器在重载解析期间为两个函数赋予相同的优先级?
问题是构造函数
template<typename T>
variant(const T&)
Run Code Online (Sandbox Code Playgroud)
的boost::variant.下面的代码再现了没有所有魔法的问题:
struct C {};
struct A {
A(const C&) {}
template<typename T>
A(const T&) {}
};
struct B {
template<typename T>
B(const T&) {}
};
int f(const A&) {
return 0;
}
int f(const B&) {
return 1;
}
int main() {
return f(C{});
}
Run Code Online (Sandbox Code Playgroud)
我认为variant只有在参数实际上可以转换为参数时才能启用构造函数,您可能希望将其作为错误引发.
一种实用的方法,如果没有使用适当的 SFINAE 显式转换来修复转换(我认为它不能在 Boost Variant 库之外优雅地完成),可能是这样的:
#include <boost/variant.hpp>
class A {};
class B {};
class C {};
class D {};
using v1 = boost::variant<A, B>;
using v2 = boost::variant<C, D>;
namespace detail {
struct F : boost::static_visitor<int> {
template <typename... T>
int operator()(boost::variant<T...> const& v) const {
return boost::apply_visitor(*this, v);
}
int operator()(A) const { return 0; }
int operator()(B) const { return 0; }
int operator()(C) const { return 1; }
int operator()(D) const { return 1; }
} _f;
}
template <typename T>
int f(T const& t) {
return detail::F()(t);
}
int main() {
std::cout << f(A{}) << "\n";
std::cout << f(B{}) << "\n";
std::cout << f(C{}) << "\n";
std::cout << f(D{}) << "\n";
std::cout << f(v1{}) << "\n";
std::cout << f(v2{}) << "\n";
}
Run Code Online (Sandbox Code Playgroud)
印刷
0
0
1
1
0
1
Run Code Online (Sandbox Code Playgroud)
假设是f(T)相同的值总是返回相同的值T,即使它是多个变体的成员