admin管理员组

文章数量:1404924

using declaration is common and works for other member methods including constructor, but for conversion operator it seems not so simple. GCC has syntax using base::operator base to introduce, which denied by Clang. Declare a forward function is accepted by both compilers, but which is a little complicated IMO.

example was tested with gcc 14.2.0 & clang 19.1.7, std=c++20. SFINAE inheritance rejects by clang.

struct base {
  template <typename T> requires (std::is_integral_v<T>)
  operator T() const { printf("to int\n"); return 0; }
};
struct derived: base {
  //using base::operator;        // denied by clang & gcc
  //using base::operator base;   // denied by clang 

  template <typename T> requires (std::is_floating_point_v<T>)
  operator T() const { printf("to float\n"); return 0; }

  // both accept
  template <typename T> requires requires { (T) std::declval<base>(); }
  operator T() const { return base::operator T(); }
};

derived D;
(int)   D;  // -> to int
(float) D;  // -> to float

using declaration is common and works for other member methods including constructor, but for conversion operator it seems not so simple. GCC has syntax using base::operator base to introduce, which denied by Clang. Declare a forward function is accepted by both compilers, but which is a little complicated IMO.

example was tested with gcc 14.2.0 & clang 19.1.7, std=c++20. SFINAE inheritance rejects by clang.

struct base {
  template <typename T> requires (std::is_integral_v<T>)
  operator T() const { printf("to int\n"); return 0; }
};
struct derived: base {
  //using base::operator;        // denied by clang & gcc
  //using base::operator base;   // denied by clang 

  template <typename T> requires (std::is_floating_point_v<T>)
  operator T() const { printf("to float\n"); return 0; }

  // both accept
  template <typename T> requires requires { (T) std::declval<base>(); }
  operator T() const { return base::operator T(); }
};

derived D;
(int)   D;  // -> to int
(float) D;  // -> to float
Share Improve this question edited Mar 9 at 15:54 Pete Becker 76.6k8 gold badges80 silver badges169 bronze badges asked Mar 9 at 8:28 YuxaiYuxai 1481 silver badge6 bronze badges 0
Add a comment  | 

2 Answers 2

Reset to default 5

Conversion function templates are one of the few things that you cannot be found by a using-declaration ([namespace.udecl] p1):

Each using-declarator in a using-declaration names the set of declarations found by lookup ([basic.lookup.qual]) for the using-declarator, except that [...], conversion function templates with a dependent return type are ignored, and [...].

The fact that GCC accepts using base::operator base; is known bug 29027, which has been unresolved since 2006.

Your workaround is already okay, but could be slightly improved:

struct base {
  // use concept instead of requires with type trait
  template <std::integral T>
  operator T() const { printf("to int\n"); return 0; }
};

struct derived: base {
  // also use concept here
  template <std::floating_point T>
  operator T() const { printf("to float\n"); return 0; }

  // use std::convertible_to, which checks that implicit conversion is possible,
  // not just explicit conversion (like in your "(T) ..." test)
  template <typename T> requires std::convertible_to<base, T>
  operator T() const { return base::operator T(); }
};

Using-declaration:

A using-declaration cannot refer to a namespace, to a scoped enumerator(until C++20), to a destructor of a base class or to a specialization of a member template for a user-defined conversion function.

So, you'll have to resort to workarounds, like the one you've currently got.

本文标签: