1

So I quite do not understand how compiler chooses function overloads. I've thought that I understand until this code:

#include <iostream>

using namespace std;

template<class T>
struct base_type:
    public T
{
};

template<class T>
void check(T (&)[sizeof(base_type<T>)/sizeof(base_type<T>)]) {std::cout << "yeah";}

template<class T>
void check(T) {std::cout << "nah";}

union U{};
struct S{};

int main()
{
    U u[1];
    S s[1];

    check(u); // compile error
    check(s);

    return 0;
}

Why compiler did not choose 2d overload of check(T) when it has failed to check size of the array reference in fist overload?

Alexander G.
  • 457
  • 2
  • 14
  • 3
    Since you went to the trouble of stating `// compiler error`, you may as well actually state verbatim what the error actually *is* as part of your question. Just saying. You may also consider including from what source you obtained this code, and what *that* source has to say about it. – WhozCraig Jun 25 '19 at 16:27
  • Possible duplicate of [Why union can't be used in Inheritance?](https://stackoverflow.com/questions/3615001/why-union-cant-be-used-in-inheritance) – Aconcagua Jun 25 '19 at 16:33
  • "overloads" is a red herring – the issue rather is that you cannot inherit from unions, see link to duplicate. – Aconcagua Jun 25 '19 at 16:36
  • 2
    This is *not at all* a duplicate of the linked question. OP is asking why SFINAE did not discard `check`, not why it is not suitable. – YSC Jun 25 '19 at 16:38
  • @YSC exactly, thank you – Alexander G. Jun 25 '19 at 17:00
  • Isn't this: `sizeof(base_type)/sizeof(base_type)` one by definition? Did you intend to determine the element size of the array instead? – doug Jun 25 '19 at 17:07
  • @doug it is, this is the point – Alexander G. Jun 25 '19 at 18:29

3 Answers3

3

Only errors from the immediate context cause a "soft" error that removes the function template from the overload set. The compiler must instantiate base_type<T> before it can evaluate sizeof(base_type<T>), and any errors resulting from that instantiation are not in the immediate context, and cause hard errors.

I am not sure what you are really trying to do, but you can probably do it using std::enable_if_t<std::is_union_v<T>> to disable the overload. The reason why this works is that the instantiation of std::enable_if which is done first causes no errors; the resulting class simply may not contain a member named type. The access to type is in the immediate context.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • Thank you for useful link. So there is no way one could check in compile-time if type can be parent without hard compiler error? (do not mention type_traits) – Alexander G. Jun 25 '19 at 18:35
  • @AlexanderG. I'm not aware of any way to do so. Even if you were willing to use type traits, there's no type trait to check whether a class is final. – Brian Bi Jun 25 '19 at 18:43
2

SFINAE occurs at declaration, not definition, and declaration of base_type<U> is fine, but produces hard error for its definition.

Standard provides traits std::is_union and std::enable_if to do SFINAE:

template<class T, std::enable_if_t<!std::is_union<T>::value, int> = 0>
void check(T (&)[1]) {std::cout << "yeah";}

And with C++17, you might even use if constexpr inside the function directly (even if simple if is ok in your simplified case):

template<class T>
void check(T (&)[1])
{
    if constexpr(std::is_union<T>::value) {
        std::cout << "nah";
    } else {
        std::cout << "yeah";
    }
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 1
    The first template could use `std::is_union` to disallow it from consideration, so it SFINAE drops out. https://en.cppreference.com/w/cpp/types/is_union – Eljay Jun 25 '19 at 16:29
  • ^^^^^ `std::enable_if_t<!std::is_union::value, void> check(T etc...)` – WhozCraig Jun 25 '19 at 16:34
2

Also I've found standard quote that might backup the answers.

Standard quote, C++11 §14.8.2/8:

Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed. — end note ]

Alexander G.
  • 457
  • 2
  • 14