Home > Enterprise >  Question about `has_const_iterator`/`has_begin_end`
Question about `has_const_iterator`/`has_begin_end`

Time:10-31

The following code comes from cxx-prettyprint , which implements detecting whether type T has a corresponding member

#include<iostream>
#include<vector>
#include<type_traits> 
using namespace std;
struct sfinae_base
{
  using yes = char;
  using no = yes[2];
};

template <typename T>
struct has_const_iterator : private sfinae_base
{
private:
  template <typename C> static yes& test(typename C::const_iterator*);
  template <typename C> static no& test(...);
public:
  static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
  using type = T;
};

template <typename T>
struct has_begin_end : private sfinae_base
{
private:
  template <typename C>
  static yes& f(typename std::enable_if<
    std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::begin)),
    typename C::const_iterator(C::*)() const>::value>::type*);

  template <typename C> static no& f(...);

  template <typename C>
  static yes& g(typename std::enable_if<
    std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::end)),
    typename C::const_iterator(C::*)() const>::value, void>::type*);

  template <typename C> static no& g(...);

public:
  static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes);
  static bool const end_value = sizeof(g<T>(nullptr)) == sizeof(yes);
};

int main()
{
  vector<int> sa{ 1,2,3,4,5 };
  cout << has_const_iterator<vector<int>>::value;
  cout<<has_begin_end<vector<int>>::beg_value;
  cout << has_begin_end<vector<int>>::end_value;
  return 0;
}

run it online

Some time later I read someone else's blog and changed it to this

#include<utility>
#include<iostream>
#include<vector>
using namespace std;
template <typename T>
struct has_const_iterator
{
private:
  template <typename U>
  static constexpr decltype(std::declval<U::const_iterator>(), bool()) test(int) { return true; }
  template <typename U>
  static constexpr bool test(...) { return false; }
public:
  static const bool value = test<T>(1); //为什么这个不对?
  using type = T;
};
template <typename T>
struct has_begin_end
{
private:
  template <typename U>
  static constexpr decltype(std::declval<U>().begin(), bool()) f(int) { return true; }
  template <typename U>
  static constexpr bool f(...) { return false; }
  template <typename U>
  static constexpr decltype(std::declval<U>().end(), bool()) g(int) { return true; }
  template <typename U>
  static constexpr bool g(...) { return false; }

public:
  static bool const beg_value = f<T>(2);
  static bool const end_value = g<T>(2);
};
int main()
{
  vector<int> sa{ 1,2,3,4,5 };
  cout << has_const_iterator<vector<int>>::value;
  cout<<has_begin_end<vector<int>>::beg_value;
  cout << has_begin_end<vector<int>>::end_value;
  return 0;
}


run it online

For the first piece of code it shows 111
For the second piece of code it shows 011
Snippet 2 was working fine a few months ago, but not now.
My question is, what's wrong with the second one,and why it was good before and now goes wrong?

Added: I found this, what does he mean by introduce ODR violations?

CodePudding user response:

If you remove all unnecessary code and then try to call the overload of has_const_iterator::test that returns true (by commenting out the other):

#include <utility>
#include <vector>
#include <iostream>

template <typename T>
struct has_const_iterator
{
private:
  template <typename U>
  static constexpr decltype(std::declval<U::const_iterator>(), bool()) test(int) { return true; }
  //template <typename U>
  //static constexpr bool test(...) { return false; }
public:
  static const bool value = test<T>(1);
};

int main()
{
  std::cout << has_const_iterator<std::vector<int>>::value;
}

then you get this error message (I cleaned it up a bit):

error: dependent-name 'U::const_iterator' is parsed as a non-type, but instantiation yields a type
| static constexpr decltype(std::declval<U::const_iterator>(), bool()) test(int) { return true; }
note: say 'typename U::const_iterator' if a type is meant

It tells you exactly what's wrong: typename is missing before U::const_iterator.

This version works:

#include <utility>
#include <vector>
#include <iostream>

template <typename T>
struct has_const_iterator
{
private:
  template <typename U>
  static constexpr decltype(std::declval<typename U::const_iterator>(), bool()) test(int) { return true; }
  template <typename U>
  static constexpr bool test(...) { return false; }
public:
  static const bool value = test<T>(1);
};

int main()
{
  std::cout << has_const_iterator<std::vector<int>>::value;
}

Demo

CodePudding user response:

You are missing a typename because U::const_iterator is a dependant name.

template <typename T>
struct has_const_iterator
{
private:
  template <typename U>
  static constexpr decltype(std::declval<typename U::const_iterator>(), bool()) test(int) { return true; }
                                         // ^^ 
  template <typename U>
  static constexpr bool test(...) { return false; }
public:
  static const bool value = test<T>(1); //为什么这个不对?
  using type = T;
};

After fixing that it prints expected results: https://godbolt.org/z/jP9G95Pb1l.

I did not find yet a compiler that produced output 111 without the typename though.

  • Related