lundi 20 mai 2019

Why is this substitution failure occuring in SFINAE-based reflection?

I have written code that detects if one object can be streamed into an std::ostream. However, while it works in clang, it fails in gcc. After simplifying the code, the problem seems to occur when two classes have operator<< defined in different namespaces.

Below is the (simplified) code (and here it is on godbolt):

#include <iostream>

namespace test_ns {
  // Define two (trivial) classes.
  class Class1 { };
  class Class2 { };

  // First class has ostream operator defined in namespace test_ns
  std::ostream & operator<<(std::ostream & out, const Class1 & v) {
    return out << "Class1 Output!";
  }
}

// Second class as ostream operator defined in global namespace.
std::ostream & operator<<(std::ostream & out, const test_ns::Class2 & v) {
  return out << "Class2 Output!";
}

namespace test_ns {
  // Simple template that always evaluates to bool (for SFINAE-based reflection)
  template <typename EVAL_TYPE> using bool_decoy = bool;

  // Two version of HasPrint that test the operator<< into ostream.
  // First version preferred if << works...
  template <typename T>
  bool HasPrint(bool_decoy<decltype( std::declval<std::ostream&>() << std::declval<T>() )>) {
    return true;
  }

  // Second version as a fallback.
  template <typename T>
  bool HasPrint(...) {
    return false;
  }

}

int main()
{
  std::cout << test_ns::HasPrint<test_ns::Class2>(true) << std::endl;
}

Here is the error I receive in gcc 9.1:

<source>: In instantiation of 'bool test_ns::HasPrint(test_ns::bool_decoy<decltype ((declval<std::basic_ostream<char, std::char_traits<char> >&>() << declval<T>()))>) [with T = test_ns::Class2; test_ns::bool_decoy<decltype ((declval<std::basic_ostream<char, std::char_traits<char> >&>() << declval<T>()))> = <type error>]':

<source>:40:55:   required from here

<source>:26:68: error: no match for 'operator<<' (operand types are 'std::basic_ostream<char>' and 'test_ns::Class2')

   26 |   bool HasPrint(bool_decoy<decltype( std::declval<std::ostream&>() << std::declval<T>() )>) {

      |                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~


(followed by a long list of candidates)

While I can (and will) refactor the code to circumvent this issue, I don't fully understand what is going wrong here. Am I improperly defining the operators, or is this a problem with gcc?





Aucun commentaire:

Enregistrer un commentaire