46 votes

Comment écrire un type de retour de fin activé par ADL ou une spécification noexcept?

Imagine que je suis en train d'écrire quelques conteneur de modèle ou de quelque chose. Et vient le temps de se spécialiser std::swap pour elle. En bon citoyen, je vais activer l'ADL en faisant quelque chose comme ceci:

template <typename T>
void swap(my_template<T>& x, my_template<T>& y) {
    using std::swap;
    swap(x.something_that_is_a_T, y.something_that_is_a_T);
}

C'est très soigné et tout. Jusqu'à ce que je veux ajouter une spécification d'exception. Mon swap est noexcept tant que le swap de T est noexcept. Donc, j'avais à écrire quelque chose comme:

template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
    noexcept(noexcept(swap(std::declval<T>(), std::declval<T>())))

Problème est, swap dans il faut l'ADL découverts swap ou std::swap. Comment puis-je gérer cela?

34voto

Johannes Schaub - litb Points 256113

Je pense que je le déplacerais dans un espace de noms séparé

 namespace tricks {
    using std::swap;

    template <typename T, typename U>
    void swap(T &t, U &u) noexcept(noexcept(swap(t, u)));
}

template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
  noexcept(noexcept(tricks::swap(std::declval<T>(), std::declval<T>()))) 
{
    using std::swap;
    swap(x.something_that_is_a_T, y.something_that_is_a_T);
}
 

Sinon, vous pouvez déplacer tout le code vers le haut dans tricks et y déléguer des tâches.

10voto

Luc Danton Points 21421

Il y a un problème similaire pour les types de retour:

// Want to be compatible with both boost::tuple and std::tuple
template<typename Tuple>
auto first(Tuple&& tuple)
-> /* ??? */
{
    // Introduce name into scope
    using std::get;
    // but ADL can still pick boost::get for boost::tuple
    return get<0>(std::forward<Tuple>(tuple));
}

À l'aide de decltype( get<0>(std::forward<Tuple>(tuple)) ) n'est pas correcte, comme get n'est pas dans la portée.

Les solutions possibles sont:

  • L'introduction d'un mannequin modèle (get dans mon exemple, swap dans votre cas) dans le cadre englobant; cela comprend la mise à l' using std::swap déclaration dans le bloc à l'intérieur de l'espace de noms, avec l'inconvénient de polluer l'espace de noms.

  • L'utilisation d'un type de trait: typename std::tuple_element<0, typename std::remove_reference<Tuple>::type>::type (en fait, celui-ci est problématique, mais pour des raisons qui n'appartiennent pas ici) dans mon exemple, et un potentiel d' is_nothrow_swappable<T>::value dans votre cas. Spécialisations permettent alors le modèle à être étendu à d'autres types en cas de besoin.

5voto

David Stone Points 3822

Plutôt que de déclarer, mais pas de la définition d'un modèle de fonction, qui semble susceptible de causer de la confusion, je voudrais écrire ma propre type de trait (qui est ce qui devrait probablement être dans la bibliothèque standard, de toute façon). Suivant l'exemple de la bibliothèque standard, je voudrais définir quelque chose comme ce qui suit:

#include <type_traits>
#include <utility>

namespace adl {

using std::swap;

template<typename T, typename U>
struct is_nothrow_swappable : std::integral_constant<
    bool,
    noexcept(swap(std::declval<T &>(), std::declval<U &>()))
> {
};

}   // namespace adl

Nous devons définir notre propre espace de noms à l'importation std::swap) en (pour éviter de donner à tout le monde), mais bien sûr, si elle était dans la bibliothèque standard, qui ne serait pas nécessaire, car ils peuvent déjà faire des appels non qualifiés de swap.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X