2014-07-16 9 views
6

Poniższy kod nie kompiluje:boost :: wariant i przeciążenie funkcja rozdzielczości

#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{}); 
} 

zarówno gcc i brzęk narzeka:

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&) { 

Biorąc pod uwagę, że nie jest możliwe skonstruowanie v2 obiektu od instancji A, dlaczego kompilator nadaje ten sam priorytet obu funkcjom w czasie działania zabezpieczenia przed przeciążeniem?

+0

'variant' ma domyślne konstruktory konwertujące szablony. Jeśli nie są one odpowiednio zabezpieczone przez 'enable_if' może to być wynik. – pmr

Odpowiedz

3

Pragmatyczne podejście, krótki mocowania konwersję z wyraźną konwersji z właściwego SFINAE (nie sądzę, można to zrobić elegancko poza Boost, Variant biblioteki), może być to:

Zobacz Live On Coliru

#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"; 
} 

Drukuje

0 
0 
1 
1 
0 
1 

założenie jest takie, że f(T) zawsze zwraca taką samą wartość dla tego samego T nawet jeśli jest członkiem więcej niż jednego wariantu

+1

Bardzo elegancka praca. – pmr

5

Problemem jest konstruktor

template<typename T> 
variant(const T&) 

z boost::variant. Poniższy kod odtwarza bez problemu całą magię:

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{}); 
} 

myślę konstruktor variant powinien być włączony tylko wtedy, gdy argument jest rzeczywiście zamienny do argumentów, warto podbicia to jako błąd.

+0

+1 dla prostej redukcji problemu – sehe

+0

Zauważ, że jest to ten sam problem z 'std :: function' ma – Yakk

+0

@Yakk Interesting. Nigdy nie wpadłem na ten problem. Czy jest tam DR? – pmr

Powiązane problemy