2015-10-23 3 views
4

Следующая программа прерывается:подталкивания вариант ссылок и равенство сравнения

#include <boost/variant.hpp> 

using variant_type = boost::variant< int&, std::string& >; 

int main() { 
    int a, b; 

    variant_type v (a), u (b); 
    v == u; 

    return 0; 
} 

с:

$ g++ -std=c++14 t.cpp && ./a.out 
a.out: /opt/boost/default/include/boost/variant/detail/forced_return.hpp:39: T boost::detail::variant::forced_return() [with T = const int&]: Assertion `false' failed. 
Aborted (core dumped) 

AFAICT вы не можете сравнить равенства между вариантами неконстантных ссылок из-за неправильного перегрузка вызова функции оператора в классе known_get. known_get создается для const T в посетителе-компаньере вместо того, что, похоже, должно было быть T (variant.hpp: 905 in v1.59.0).

Я что-то упустил?

+0

Я ничего не вижу о вариантах ссылок в документации по ускорению. так UB? –

+0

Я так не думаю. Хотя это правда, что документы и esp. в учебнике ничего не говорится о хранении ссылок, кажется, нет препятствий для использования их вариантов. – Engineerist

ответ

3

Я думаю, что это ошибка Boost.

type requirements здесь:

  • CopyConstructible или MoveConstructible.
  • Деструктор поддерживает гарантию отсутствия безопасности.
  • Завершить создание экземпляра шаблона варианта. (См boost::recursive_wrapper<T> для типа обертки, который принимает неполные типы, чтобы позволить рекурсивные типы вариантные.)

, а также:

  • EqualityComparable: variant сама по себе EqualityComparable тогда и только тогда, когда каждый из его ограниченных типов отвечает требованиям концепции.

ссылочный тип копия конструктивны, не перекидной разрушаемость, полное и равенство сопоставимыми. Таким образом, у нас все хорошо. Вопрос заключается в том, что посетитель, используемый в реализации является:

template <typename T> 
bool operator()(const T& rhs_content) const 
{ 
    // Since the precondition ensures lhs and rhs types are same, get T... 
    known_get<const T> getter; 
    const T& lhs_content = lhs_.apply_visitor(getter); 

    // ...and compare lhs and rhs contents: 
    return Comp()(lhs_content, rhs_content); 
} 

То есть, мы используем const T как известный добытчик.Это хорошо для не-ссылочных типов, но неверно для ссылочных типов, так как known_get утверждает, если он получает неправильный тип:

T& operator()(T& operand) const BOOST_NOEXCEPT 
{ 
    return operand; 
} 

template <typename U> 
T& operator()(U&) const 
{ 
    // logical error to be here: see precondition above 
    BOOST_ASSERT(false); 
    return ::boost::detail::variant::forced_return<T&>(); 
} 

С int&, эти перегрузки стали:

const int& operator()(const int&) const; 
const int& operator()(int&) const; [ U = int ] 

Вторая перегрузка предпочтительнее, поскольку тип, на который он ссылается, будет менее const-квалифицированным, чем не-шаблонная перегрузка. Вот почему вы получаете утверждение, и это поведение явно неверно. Мы должны иметь возможность сравнивать ссылки!

Чем проще решение было бы отбросить const с от comparer и просто использовать:

template <typename T> 
bool operator()(T& rhs_content) const 
{ 
    known_get<T> getter; 
    T& lhs_content = lhs_.apply_visitor(getter); 

    return Comp()(lhs_content, rhs_content); 
} 

Это будет работать для ссылочных типов, а также const типов.

+0

Да, я беспокоился, что у меня пропало что-то очевидное в том, почему known_get создается для const T вместо T. Я попытаюсь записать ошибку с boost, а затем получить вернуться к голосованию. – Engineerist

0

ОК, я подумал об этом (и еще один взгляд на документацию).

Синтаксис для boost :: variant не отображает оператора ==, определенного для варианта.

Это приводит меня к предположению, что правильный подход для сравнения осуществляется через двоичного посетителя.

вот ваш (измененный) код снова, который компилируется на моей машине (яблоко clang), ваш код также разбил мой компилятор.

#include <string> 
#include <boost/variant.hpp> 

using variant_type = boost::variant< int&, std::string& >; 

struct is_equal 
{ 
    // compare int with int 
    bool operator()(const int& l, const int& r) const { 
     return l == r; 
    } 

    // compare string with string 
    bool operator()(const std::string& l, const std::string& r) const { 
     return l == r; 
    } 

    // anything else compared with anything else compares false.   
    template<class X, class Y> 
    bool operator()(const X&, const Y&) const { 
     return false; 
    } 
}; 

int main() { 
    int a = 0, b = 0; 

    variant_type v = a, u = b; 

    bool ok = boost::apply_visitor(is_equal(), v, u); 
// bool ok = (v == u); 

    return 0; 
} 
+1

Ричард, есть оператор ==, определенный в строке 2236 в файле variant.hpp. Ваше решение - это то, что я бы тоже реализовал, если бы этот оператор еще не был там. На данный момент я помешал этой ошибке возможной ошибке, потому что если вы измените const T на T в списке аргументов шаблона known_get в строке 905 варианта.hpp, код компилируется и запускается нормально. – Engineerist

+0

согласился, если оператор == должен быть частью публичного интерфейса, тогда он выглядит как ошибка. Удивительно, что он сбрасывает как clang, так и gcc :) –