"private" access specifier not respected in overloaded SFINAE context
HexadigmSystems opened this issue · 7 comments
Given the following code (run it here), where "Base::Whatever" is private (both overloads), why does Clang correctly display false except when (both) "T" is "Derived" and function "Whatever" is overloaded. This appears to be erroneous behavior but it's a fuzzy area in this context (but see behavior table further below):
#include <type_traits>
#include <iostream>
class Base
{
private: // Defaults to this anyway but being explicit
void Whatever(int)
{
}
void Whatever(int, float)
{
}
};
class Derived : public Base
{
};
template <typename T, typename U, typename = void>
struct HasFuncWhatever : std::false_type
{
};
template <typename T, typename U>
struct HasFuncWhatever<T,
U,
std::void_t<decltype(static_cast<U T::*>(&T::Whatever))>
>
: std::true_type
{
};
int main()
{
using T = Derived;
using U = void (int);
std::cout << std::boolalpha << HasFuncWhatever<T, U>::value;
return 0;
}
Here's the behavior of the 3 compilers I tested (only MSVC presumably gets it right):
T "Whatever" overloaded? Clang Displays MSVC Displays GCC Displays
- ---------------------- -------------- ------------- ------------
Base No false (correct) false (correct) false (correct)
Base Yes false (correct) false (correct) Fails compilation due to private access (incorrect)
Derived No false (correct) false (correct) false (correct)
Derived Yes true (incorrect) false (correct) Fails compilation due to private access (incorrect)
Unless this is explicitly mentioned in the standard somewhere, or it's considered undefined behavior (implementation defined), the call to "&T::Whatever" in the partial specialization of "HasFuncWhatever" should always presumably fail since "Whatever" is private. The primary template should therefore always kick in so the code should always display false.
@llvm/issue-subscribers-clang-frontend
Author: Hexadigm Systems (HexadigmSystems)
#include <type_traits>
#include <iostream>
class Base
{
private: // Defaults to this anyway but being explicit
void Whatever(int)
{
}
void Whatever(int, float)
{
}
};
class Derived : public Base
{
};
template <typename T, typename U, typename = void>
struct HasFuncWhatever : std::false_type
{
};
template <typename T, typename U>
struct HasFuncWhatever<T,
U,
std::void_t<decltype(static_cast<U T::*>(&T::Whatever))>
>
: std::true_type
{
};
int main()
{
using T = Derived;
using U = void (int);
std::cout << HasFuncWhatever<T, U>::value;
return 0;
}
Here's the behavior of the 3 compilers I tested (only MSVC presumably gets it right):
Clang behavior
T "Whatever" overloaded? Clang Displays MSVC Displays GCC Displays
- ---------------------- -------- -------- --------
Base No false (correct) false (correct) false (correct)
Base Yes false (correct) false (correct) Fails compilation due to private access (incorrect)
Derived No false (correct) false (correct) false (incorrect)
Derived Yes true (incorrect) false (correct) Fails compilation due to private access (incorrect)
Unless this is explicitly mentioned in the standard somewhere, or it's considered undefined behavior (implementation defined), the call to "&T::Whatever" in the partial specialization of "HasFuncWhatever" should always presumably fail since "Whatever" is private. The primary template should therefore always kick in so the code should always display false.
This happens in non-SFINAE contexts too:
https://godbolt.org/z/sKvqq1qYx:
struct Base {
private:
int f();
int f(int); // Does not compile if this is removed
};
struct Derived : public Base {};
int main() {
int(Derived::* _)() = &Derived::f;
}Thanks for the quick reply. Definitely simplifies things, and I probably should have checked but was focused on my specific issue. In any case, it appears to be a bug unless someone can confirm otherwise (may be implementation defined behavior but looks very suspect and definitely inconvenient).
No one allows this case: https://godbolt.org/z/c4f5K18v1
struct Base {
private:
int f();
int f(int); // Does not compile if this is removed
};
struct Derived : public Base {};
int main() {
int(Base::* _)() = &Base::f;
}
Thanks for the feedback. Didn't really look into MitalAshok's post in too much detail (though you've changed it a bit) but my original post occurs in an SFINAE context so it's a murky area. On the surface Clang and GCC look problematic but a language lawyer may be required to settle it.
diag is suppressed here
llvm-project/clang/lib/Sema/SemaExpr.cpp
Lines 3176 to 3207 in dec0781
@Backl1ght Thanks for the info (does it mean it's now a confirmed bug?). Note that I reported GCC's own issue here (as described in the behavior table of my original post above). That bug was confirmed by GCC (see Andrew Pinski's response at latter link). Someone there also added a link to this Clang post in the See Also section.