2016-10-13 1 views
6

Je suis en train de définir et de visiter un « récursive » boost::variant en utilisant une classe wrapper incomplete et std::vector comme mes techniques d'indirection. Ma mise en œuvre fonctionne avec libstdC++, mais pas avec libC++.récursive la définition et la visite d'un `boost :: variant`` l'aide d'un std :: vECTOR` contenant un type incomplet - libstdC++ vs libC++


C'est la façon dont je suis en train de définir ma variante:

struct my_variant_wrapper; 

using my_variant_array = std::vector<my_variant_wrapper>; // <- indirection here 
using my_variant = boost::variant<int, my_variant_array>; 

struct my_variant_wrapper 
{ 
    my_variant _v; 

    template <typename... Ts> 
    my_variant_wrapper(Ts&&... xs) : _v(std::forward<Ts>(xs)...) { } 
}; 

J'utilise std::vector pour introduire indirection (de sorte que l'allocation dynamique empêchera my_variant d'avoir une taille infinie).

Je suis tout à fait confiant que je suis autorisé à utiliser std::vector<my_variant_wrapper>, où my_variant_wrapper est un incomplete type, en raison de paper N4510("Minimal incomplete type support for standard containers"):

  • Le document a été approuvé, selon WG21's 2015 page. Les caractéristiques ont toujours été supportées dans libstdC++, selon this page. Il a été implémenté dans libC++ 3.6, selon this page.


Je visitais alors la variante comme suit:

struct my_visitor 
{ 
    void operator()(int x) const { } 
    void operator()(const my_variant_array& arr) const 
    { 
     for(const auto& x : arr)    
      boost::apply_visitor(*this, x._v);    
    } 
}; 

int main() 
{ 
    my_variant v0 = my_variant_array{ 
     my_variant{1}, my_variant{2}, my_variant_array{ 
      my_variant{3}, my_variant{4} 
     } 
    }; 

    boost::apply_visitor(my_visitor{}, v0); 
} 

A minimal complete example is available on coliru.


  • J'utilise les drapeaux suivants:

    std = C++ 1z -Wall -Wextra -Wpedantic

  • BOOST_VERSION évalue à 106100.

Le code:

  • Compile et exécute comme prévu sur:

    • g ++ (versions testées: 6.1 et 7), avec libstdC++.

    • clang ++ (versions testées: 3.8), avec libstdC++.

    • (comme un bonus, cela fonctionne aussi avec std::variant en apportant les modifications appropriées!)

  • ne réussit pas à compiler sur:

    • clang ++ (versions testées : 3.8, 4), avec libC++.

Ceci est l'erreur que je reçois lors de la compilation sur clang ++ avec libC++:

In file included from prog.cc:2: 
In file included from /usr/local/boost-1.61.0/include/boost/variant.hpp:17: 
/usr/local/boost-1.61.0/include/boost/variant/variant.hpp:1537:28: error: no matching member function for call to 'initialize' 
       initializer::initialize(
       ~~~~~~~~~~~~~^~~~~~~~~~ 
/usr/local/boost-1.61.0/include/boost/variant/variant.hpp:1692:9: note: in instantiation of function template specialization 'boost::variant<int, std::__1::vector<my_variant_wrapper, std::__1::allocator<my_variant_wrapper> > >::convert_construct<my_variant_wrapper>' requested here 
     convert_construct(operand, 1L); 
     ^
prog.cc:15:38: note: in instantiation of function template specialization 'boost::variant<int, std::__1::vector<my_variant_wrapper, std::__1::allocator<my_variant_wrapper> > >::variant<my_variant_wrapper>' requested here 
    my_variant_wrapper(Ts&&... xs) : _v(std::forward<Ts>(xs)...) { } 
            ^
/usr/local/libcxx-head/include/c++/v1/memory:1783:31: note: in instantiation of function template specialization 'my_variant_wrapper::my_variant_wrapper<my_variant_wrapper &>' requested here 
      ::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...); 
          ^
/usr/local/libcxx-head/include/c++/v1/memory:1694:18: note: in instantiation of function template specialization 'std::__1::allocator<my_variant_wrapper>::construct<my_variant_wrapper, my_variant_wrapper &>' requested here 
      {__a.construct(__p, _VSTD::forward<_Args>(__args)...);} 
       ^

... 

The full error is available on wandbox.


Pourquoi le code ne compilent avec libC++?(Serait-ce un défaut dans la mise en œuvre libc de ++ 's N4510 qui doit être rapporté?)

L'erreur semble indiquer que la variante ne parvient pas à détecter ce que les membres doivent être initialisées, mais honnêtement, je ne pouvais pas faire beaucoup de sens de cela. Je suis également confus par le fait que l'utilisation libstdC++(avec la même version de boost) fonctionne comme prévu.

Répondre

6

J'ai vu cela dans le backtrace:

Note: instanciation de spécialisation de modèle de fonction « my_variant_wrapper::my_variant_wrapper<my_variant_wrapper &> » demandé ici

qui est une indication claire que votre modèle de constructeur est Détournement la constructeur de copie. Contraindre, et votre problème disparaît.


La différence entre les implémentations est due à la façon de vector de » copier des copies du constructeur les éléments. libstdC++ traite les éléments sources comme const:

vector(const vector& __x) 
    : _Base(__x.size(), 
    _Alloc_traits::_S_select_on_copy(__x._M_get_Tp_allocator())) 
{ 
    this->_M_impl._M_finish = 
    std::__uninitialized_copy_a(__x.begin(), __x.end(), 
           this->_M_impl._M_start, 
           _M_get_Tp_allocator()); 
} 

Parce que begin() et end() sont appelés const vector& x, ils reviennent itérateurs constants.

libC++ traite les éléments sources comme non const:

template <class _Tp, class _Allocator> 
vector<_Tp, _Allocator>::vector(const vector& __x) 
    : __base(__alloc_traits::select_on_container_copy_construction(__x.__alloc())) 
{ 
#if _LIBCPP_DEBUG_LEVEL >= 2 
    __get_db()->__insert_c(this); 
#endif 
    size_type __n = __x.size(); 
    if (__n > 0) 
    { 
     allocate(__n); 
     __construct_at_end(__x.__begin_, __x.__end_, __n); 
    } 
} 

__begin_ et __end_ sont pointer s, et depuis const est peu profonde, la const ness de __x ne fait pas la const pointée.

Les deux sont conformes, car CopyInsertable nécessite une capacité de copie à partir des sources const et non const. Cependant, votre modèle ne fait que détourner la copie de non-const (car il perd la copie de const cas par la condition de départage modèle/non-modèle), de sorte que vous ne voyez que le problème dans libC++.

+0

Génial, merci! [Il compile avec ** libC++ **] (http://melpon.org/wandbox/permlink/TCBfoFPrEZHFE8G0) lorsqu'il est contraint. Je suis toujours perplexe sur la raison pour laquelle cette erreur ne se produit qu'avec libC++. Pourriez-vous élaborer sur ce point? –

+1

@VittorioRomeo Juste passé un peu plus de temps à creuser - voir modifier. –

+1

Le vecteur de libC++ tente de copier-construire les éléments vectoriels à l'aide d'une référence de lvalue non-const, alors que libstdC++ passe une référence const lvalue. Parce que libC++ utilise une référence non-const, il choisit le constructeur de modèle au lieu du constructeur de copie. – EricWF