最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - C++20 SFINAE trailing vs leading requires in member function in a class template - Stack Overflow

programmeradmin2浏览0评论

I have a C++17 piece of SFINAE code

#include <type_traits>

template<typename>
constexpr bool dependentFalse = false;

template<typename T>
struct IAmEmpty
{
    template<typename U=T, std::enable_if_t<dependentFalse<U>>* =nullptr>
    auto deleteMe()
    {}

};

template struct IAmEmpty<int>;

I am trying to rework that to C++20 and I wonder:

#include <type_traits>

template<typename T>
struct IAmEmpty
{
    template<typename U=T>
    requires false
    void deleteMe() {}

    void deleteMeToo() requires false {}
};

template struct IAmEmpty<int>;

As far as I understand it, leading requires is close to pre-C++20 SFINAE achieved using e.g. enable_if, void_t and the like. Therefore deleteMe is actually a member function template. deleteMeToo is not a function template (or am I wrong here?). Live example:

Are there any situations when deleteMe and deleteMeToo actually behave differently and should one care when preferring trailing requires over the leading one?

I have a C++17 piece of SFINAE code

#include <type_traits>

template<typename>
constexpr bool dependentFalse = false;

template<typename T>
struct IAmEmpty
{
    template<typename U=T, std::enable_if_t<dependentFalse<U>>* =nullptr>
    auto deleteMe()
    {}

};

template struct IAmEmpty<int>;

https://godbolt./z/vWW3favWj

I am trying to rework that to C++20 and I wonder:

#include <type_traits>

template<typename T>
struct IAmEmpty
{
    template<typename U=T>
    requires false
    void deleteMe() {}

    void deleteMeToo() requires false {}
};

template struct IAmEmpty<int>;

As far as I understand it, leading requires is close to pre-C++20 SFINAE achieved using e.g. enable_if, void_t and the like. Therefore deleteMe is actually a member function template. deleteMeToo is not a function template (or am I wrong here?). Live example: https://godbolt./z/rGzx8dz3f

Are there any situations when deleteMe and deleteMeToo actually behave differently and should one care when preferring trailing requires over the leading one?

Share Improve this question edited Mar 31 at 8:31 Jarod42 219k15 gold badges196 silver badges330 bronze badges asked Mar 31 at 6:35 alagneralagner 4,0841 gold badge14 silver badges28 bronze badges 6
  • 1 "preferring trailing requires over the leading one" template <..> requires (..) void foo(); is equivalent to template <..> void foo(); requires (..). See Requires_clauses – Jarod42 Commented Mar 31 at 7:41
  • "Are there any situations when deleteMe and deleteMeToo actually behave differently" One is function template (with non deducible type) and the other not. Which is un related to requires clauses. – Jarod42 Commented Mar 31 at 7:45
  • @Jarod42 One is function template (with non deducible type) and the other not OK, so both are functionally equivalent? I am asking in context of reworking C++17 SFINAE code into C++20. Edited question for more context. – alagner Commented Mar 31 at 8:16
  • Template functions are not equivalent to non-template functions, (but it is unrelated to requires clauses). template function are instantiated when used or when explicitly instantiated, regular function still exists. – Jarod42 Commented Mar 31 at 8:35
  • 1 Are you looking for void deleteMe() = delete; (which works since C++11) (or the requirement was just an example). = delete can be applied to non-special methods too. – Jarod42 Commented Mar 31 at 8:36
 |  Show 1 more comment

1 Answer 1

Reset to default 3

when preferring trailing requires over the leading one?

template <..> requires (..) void foo(); is equivalent to template <..> void foo() requires (..); So it is just style (or possibly convenience to use function parameter in requires).

Non template function only allows requires after.

Are there any situations when deleteMe and deleteMeToo actually behave differently and should one care?

One is template function whereas the other is not (they are still template entities, from the class).

And template and non-template functions have some differences. Template has/allows

  • two-phase lookup
  • point of Instantiation
  • overloads which differ only by their return type
  • ...

For migration, there are no points to use template (which was added only for SFINAE), so

// C++17
template<typename T>
struct IAmEmpty
{
    template <typename U=T, std::enable_if_t<some_condition_v<U>>* = nullptr>
    auto deleteMe()
    {}
};

would become

// C++20
template<typename T>
struct IAmEmpty
{
    auto deleteMe() requires some_condition_v<T> {}
};

You might consider to turn traits some_condition_v into concepts to allow subsumption.

发布评论

评论列表(0)

  1. 暂无评论