2014-07-16 8 views
6

Der folgende Code nicht kompiliert:boost :: variant und Funktionsüberladungsauflösung

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

beiden gcc und Klirren klagt mit:

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

Da nicht möglich ist, ein v2 Objekt zu konstruieren von einer A Instanz, warum der Compiler beiden Funktionen bei der Überladungsauflösung die gleiche Priorität gibt?

+0

"Variante" hat implizite konvertierte Konstruktoren Templates. Wenn diese nicht ausreichend mit 'enable_if' geschützt sind, könnte dies das Ergebnis sein. – pmr

Antwort

3

Ein pragmatischer Ansatz, kurz vor der Umsetzung mit expliziter Konvertierung mit dem richtigen SFINAE Festsetzung (Ich glaube nicht, es elegant außerhalb der Boost-Variant-Bibliothek durchgeführt werden kann), das sein könnte:

es Siehe 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"; 
} 

Drucke

0 
0 
1 
1 
0 
1 

Die Annahme ist, dass f(T) liefert immer den gleichen Wert für die gleiche T auch wenn es das Mitglied von mehr als einer Variante ist

+1

Sehr elegante Work-around. – pmr

5

Das Problem ist der Konstruktor

template<typename T> 
variant(const T&) 

von boost::variant. Der folgende Code reproduziert das Problem ohne die ganze Magie:

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

ich denke, die variant Konstruktor sollte nur, wenn das Argument tatsächlich umwandelbar ist auf die Argumente aktiviert sein, können Sie diese als Fehler zu erhöhen möchten.

+0

+1 für die einfache Reduzierung des Problems – sehe

+0

Beachten Sie, dass dies das gleiche Problem ist "Std :: Funktion" hat – Yakk

+0

@ Yakk Interessant. Ich bin nie auf dieses Problem gestoßen. Gibt es eine DR dafür? – pmr

Verwandte Themen