Why does this SFINAE not work with enable_if when one conditional branch is inherited from the base class?

江枫思渺然 提交于 2021-01-28 06:25:30

问题


#include <bits/stdc++.h>
#include <type_traits>

// Type your code here, or load an example.
template <typename Types>
class C1 {
public:
    using A=typename Types::A;
    using B=typename Types::B;

    template <typename Dummy = void>
    inline typename std::enable_if<std::is_same<A, B>::value, Dummy>::type f() { }

};

template <typename Types>
class C2 : public C1<Types> {
public:
    using A=typename Types::A;
    using B=typename Types::B;

    template <typename Dummy = void>
    inline typename std::enable_if<!std::is_same<A, B>::value, Dummy>::type f() { }

};

template <typename Types>
class C3 : public C2<Types> {
public:
    using A=typename Types::A;
    using B=typename Types::B;
};


struct Types{
    using A = int;
    using B = int;
};

int main() {
    C3<Types> c;
    c.f();
    return 0;
}

When I try to compile the above code when A and B are not same, I get the following error:

<source>: In function 'int main()':
<source>:42:9: error: no matching function for call to 'C3<Types>::f()'
   42 |     c.f();
      |         ^
<source>:23:77: note: candidate: 'template<class Dummy> typename std::enable_if<(! std::is_same<typename Types::A, typename Types::B>::value), Dummy>::type C2<Types>::f() [with Dummy = Dummy; Types = Types]'
   23 |     inline typename std::enable_if<!std::is_same<A, B>::value, Dummy>::type f() { }
      |                                                                             ^
<source>:23:77: note:   template argument deduction/substitution failed:
<source>: In substitution of 'template<class Dummy> typename std::enable_if<false, Dummy>::type C2<Types>::f<Dummy>() [with Dummy = void]':
<source>:42:9:   required from here
<source>:23:77: error: no type named 'type' in 'struct std::enable_if<false, void>'

Note that the code I have presented is not the exact code I use but a minimal reproducible example

EDIT: Put up a minimal reproducible example using godbolt in place of the earlier for a better understanding of the situation


回答1:


As always with such problems, it falls down to the very definition of SFINAE. The S stands for "substitution", which happens into the template that we are trying to instantiate. That template is the member f, and not C.

Even though C is a template also, and both A and B are dependent types in C, they are not dependent type when f is instantiated. They are already known. As such, the condition std::is_same<A, B>::value is not value dependent on any template parameter of f. It doesn't depend on substation into f. This trips the following clause in the C++11 standard (taken from the last draft prior to publication):

[temp.res] (emphasis mine)

8 Knowing which names are type names allows the syntax of every template definition to be checked. No diagnostic shall be issued for a template definition for which a valid specialization can be generated. If no valid specialization can be generated for a template definition, and that template is not instantiated, the template definition is ill-formed, no diagnostic required.

This means that whatever Types is, if it doesn't uphold the condition of f, then the very definition of f (without even being instatiated), is already ill-formed whenever C is instantiated. A diagnostic is not required for this in general (since checking it is intractable in the general case), but compilers can diagnose it early often enough, and will tell you about the problem.

Now, as to how to fix it, just make the condition of f value dependent on its own template parameter. A simple re-write can be

template <bool Dummy = std::is_same<A, B>::value>
inline auto f(vector<int>& ctx, const string& r) -> 
  typename std::enable_if<Dummy>::type { }

Now the condition depends on substation in the correct context.


Of course, even if you fix the SFIANE problem, you still need to make sure the overload set is composed of the correct members. The f in C2 hides the f in C1. Add a using declaration to C2 so it's still a candidate

using C1<Types>::f;


来源:https://stackoverflow.com/questions/65475615/why-does-this-sfinae-not-work-with-enable-if-when-one-conditional-branch-is-inhe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!