Take the following code, which is characterized by
Reliance on ADL for a specific behavior (volume)
Using decltype for return type and relying on SFINAE to discard extra overloads
namespace Nature {
struct Plant {};
double volume(Plant){ return 3.14; }
}
namespace Industrial {
struct Plant {};
double volume(Plant) { return 100; }
}
namespace SoundEffects {
// A workaround for GCC, but why?
////template<class T> void volume();
template<class aSound>
auto mix(aSound& s) -> decltype(volume(s)*0.1)
{
return volume(s)*.1;
}
struct Samples {
Nature::Plant np;
Industrial::Plant ip;
};
inline double mix(const Samples& s) {
return mix(s.np) + mix(s.ip);
}
}
int main()
{
SoundEffects::Samples s;
assert( mix(s) == 100*.1 + 3.14*.1 );
}
The code as presented (without the template<class T> void volume() line), VS 2012 and clang 3.5 compiles successfully, and runtime is as expected. However, GCC 4.7.2 says:
template-function-overload.cpp: In substitution of 'template<class aSound> decltype ((volume(s) * 1.0000000000000001e-1)) SoundEffects::mix(aSound&) [with aSound = SoundEffects::Samples]':
template-function-overload.cpp:46:4: required from here
template-function-overload.cpp:23:9: error: 'volume' was not declared in this scope
template-function-overload.cpp:23:9: note: suggested alternatives:
template-function-overload.cpp:9:11: note: 'Nature::volume'
template-function-overload.cpp:14:11: note: 'Industrial::volume'
With the extra template volume line, all three compile and run fine.
So, there is clearly a compiler defect here. My question is, which compiler is the defective one? And which C++ standard is being violated?
This was a bug which was fixed since GCC 4.8. Here's a simplified version of the code that gives the same error:
template<class T>
auto buzz(T x) -> decltype(foo(x));
void buzz(int);
int main() {
buzz(5); // error: 'foo' was not declared in this scope
}
In mix(s), both overloads of SoundEffects::mix are compiled into the set of candidate overloads via ADL (SoundEffects is an associated namespace of SoundEffects::Sample). The function template overload is evaluated for viability. The error occurs because volume(s) cannot be resolved to a suitable overload either via pure unqualified lookup or ADL for Sample.
The reason this should pass is that when lookup fails a substitution failure should occur (since volume(s) is dependent) and the template should be rejected from overload resolution. This would leave mix(const Sample&) as the only viable overload to select. The fact that there is a hard error is clearly a sign that this GCC version has a faulty SFINAE implementation.
Related
I'm getting the following error
min.cpp:17:30: error: no viable conversion from '<overloaded function type>' to 'Container::UnaryFun' (aka 'function<double (double)>')
this->addFunction("abs", abs);
when trying to compile the following code:
#include <cmath>
#include <string>
#include <functional>
class Test
{
public:
using UnaryFun = std::function<double (double)>;
Test()
{
this->addFunction("abs", abs);
}
auto addFunction(const std::string& name, UnaryFun fun) -> void
{
// ...
}
};
auto main() -> int {
Test eval;
return 0;
}
I've tried to check the declaration of std::abs for argument double and return type double and looks like this:
inline _LIBCPP_INLINE_VISIBILITY double abs(double __lcpp_x) _NOEXCEPT {
return __builtin_fabs(__lcpp_x);
}
in /usr/local/Cellar/llvm/15.0.7_1/include/c++/v1/stdlib.h.
It is accesible specifically for the double type. I've checked this by adding:
double a = 5;
double b = std::abs(a);
and this compiles without problems or conversion warnings.
I've tried to declare my own abs function like so:
inline double xabs(double val)
{
return val < 0 ? -val : val;
}
and then change the following code like so to use this new xabs instead of std::abs
this->addFunction("abs", xabs);
and after this change, the code compiles.
Any ideas why the code with std::abs doesn't compile?
My environment:
OS: Mac OS 12.6
Compiler:
Apple clang version 14.0.0 (clang-1400.0.29.202)
Target: x86_64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Command to compile: g++ -std=c++2a -o min min.cpp
Update based on comments
I dug a bit deeper, and it seems that there is a problem with how std::function is declared, which led to the problem above.
If I declare addFunction like so, without std::function, the problem disappears.
auto addFunction(const std::string& name, double (*fun)(double)) -> void
{
}
This means that the compiler cannot figure out the matching abs if std::function is used but it can identify the matching overload if the type of the function is described directly without std::function.
The problem is that, since it has multiple overloads, std::abs doesn't have a single type. That means that the compiler can't select a std::function constructor to use to convert it since it can't deduce a type for the constructor's template parameter.
There are a couple of ways to get around that:
Use a cast:
addFunction("abs", std::static_cast<double(*)(double)>(std::abs));
Wrap it in a lambda:
addFunction("abs", [](double d) { return std::abs(d); });
As you've done, wrap it in a non-overloaded function
Consider (the name of the file is hello.cpp) this code; the idea is to engineer a safe casting of numeric types without loss or overflow. (I'm porting some code from MSVC to g++).
#include <cstdint>
#include <iostream>
template<
typename T/*the desired type*/,
typename/*the source type*/ Y
> T integral_cast(const Y& y)
{
static_assert(false, "undefined integral cast");
}
// Specialisation to convert std::uint32_t to double
template<>
inline double integral_cast(const std::uint32_t& y)
{
double ret = static_cast<double>(y);
return ret;
}
int main()
{
std::uint32_t a = 20;
double f = integral_cast<double>(a); // uses the specialisation
std::cout << f;
}
When I compile with gcc 8.3 by typing g++ -o hello hello.cpp I get the error error: static assertion failed: undefined integral cast.
This means that g++ is always compiling the unused template code.
Note that MSVC compiles this (which is nice since it allows me to spot any integral cast specialisations that I haven't considered).
Clearly I'm missing something. But what?
GCC isn't really "instantiating" or "compiling" the base function template. If it was, you would have two compiled functions with the same name and parameter list. As #Raymond Chen pointed out in the comments, GCC is permitted, but not required, to raise an error for templates that don't have any valid instantiation.
For example:
template<
typename T/*the desired type*/,
typename/*the source type*/ Y
> T integral_cast(const Y& y)
{
static_assert(sizeof(Y) == 1);
};
Won't raise an error in the example you give (because it has a valid instantiation and is not instantiated).
I suspect GCC just needs to substitute the types into the base template for overload resolution, so it really just needs the declaration, not the definition.
You can get the behavior you want by using a deleted definition:
template<
typename T/*the desired type*/,
typename/*the source type*/ Y
> T integral_cast(const Y& y) = delete;
In trying to understand c++17 compliant code, i am confused by the following code in
which the function uses the value from integral_constant type argument in the
trailing return type. (please feel free to correct my terminolgy etc, trying to learn)
Two simple versions are illustrated below , with and without a decltype on the
argument in the trailing return.
Using Compiler Explorer https://godbolt.org/z/vqmzhu
The first (bool_from1) compiles ok on
MSVC 15.8; /std:c++17, /O2, /permissive-
and clang 8.7.0.0 and gcc 8.2; -std=c++17, -O2, -pedantic
with correct assembler output
The second (bool_from2) errors out on gcc,
It also shows Intellisense errors in VS but compiles without error.
I could not find anything in cppreference or standard draft, etc that would indicate to me that the decltype(atype) would be required for conforming code, however...???
My question would be is the decltype required.
Are my compiler flags correct for c++17 conformance checking.
CODE:
#include <utility>
namespace ns {
volatile bool vb;
template<bool B> struct bool_ : std::bool_constant<B> {};
// using decltype(btype) in trailing return compiles in all 3
template<typename Bool> constexpr auto bool_from1(Bool btype)
-> bool_<decltype(btype)::value> {
return bool_<btype.value>{};
}
void test1() {
static_assert( // simple test
bool_from1(std::true_type{}).value
);
vb = bool_from1(std::true_type{}).value; // check output
}
// without decltype in trailing return compile in VS and clang
// but errors out in gcc; and VS shows Intelisense errors but compiles
template<typename Bool>
constexpr auto bool_from2(Bool btype)
// ^ gcc 8.2 error: deduced class type 'bool_' in function return type
-> bool_<btype.value> {
// ^ gcc: invalid template-id; use of paramter outside function body before '.'
//^ VS Intellisense on btype: <error-constant>; a paramter is not allowed
return bool_<btype.value>{};
}
void test2() {
static_assert(
bool_from2(std::true_type{}).value
//^ gcc: bool_from1 was not declared in this scope
);
vb = bool_from2(std::true_type{}).value; // check output
}
}
This looks like a gcc bug and bug report: "Trailing return types" with "non-type template arguments" which could be "constant expressions" produce a parsing error seems to fit this case:
Consider the following snippet:
template <int>
struct bar {};
template <class I>
auto foo(I i) -> bar<i()> { return {}; }
int main()
{
foo([]{ return 1; }); // (0)
}
This compiles and works as intended on clang++5, but produces a
compile-time error on g++7:
prog.cc:5:25: error: template argument 1 is invalid
auto foo(I i) -> bar<i()> { return {}; }
This question already has an answer here:
Operator cast, GCC and clang: which compiler is right?
(1 answer)
Closed 6 years ago.
Consider following program:
struct S {
using T = float;
operator T() { return 9.9f; }
};
int main() {
S m;
S::T t = m;
t = m.operator T(); // Is this correct ?
}
The program compiles fine in g++ ( See live demo here )
But it fails in compilation in clang++, MSVC++ & Intel C++ compiler
clang++ gives following errors ( See live demo here )
main.cpp:8:20: error: unknown type name 'T'; did you mean 'S::T'?
t = m.operator T(); // Is this correct ?
^
S::T
main.cpp:2:11: note: 'S::T' declared here
using T = float;
MSVC++ gives following errors ( See live demo here )
source_file.cpp(8): error C2833: 'operator T' is not a recognized operator or type
source_file.cpp(8): error C2059: syntax error: 'newline'
Intel C++ Compiler also rejects this code ( See live demo here )
So, the question is which compiler is right here ? Is g++ incorrect here or other 3 compilers are incorrect here ? What C++ standard says about this ?
[basic.lookup.classref]/7:
If the id-expression is a conversion-function-id, its conversion-type-id is first looked up in the class of the object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire
postfix-expression. In each of these lookups, only names that denote types or templates whose specializations are types are considered. [ Example:
struct A { };
namespace N {
struct A {
void g() { }
template <class T> operator T();
};
}
int main() {
N::A a;
a.operator A(); // calls N::A::operator N::A
}
— end example]
This indicates that the example could be fine, although in the above example, A has previously been declared as a type name, visible to main.
This was discussed in core issue 156, filed all the way back in 1999:
How about:
struct A { typedef int T; operator T(); };
struct B : A { operator T(); } b;
void foo() {
b.A::operator T(); // 2) error T is not found in the context
// of the postfix-expression?
}
Is this interpretation correct? Or was the intent for this to be an
error only if T was found in both scopes and referred to different
entities?
Erwin Unruh: The intent was that you look in both contexts. If you find it only once, that's the symbol. If you find it in both, both symbols must be "the same" in some respect. (If you don't find it, its an error).
So I'd say that Clang is wrong: the intent, as expressed in the wording to some extent, is that we find T, even if only in the class.
This compiles with out problems in VS 2009? Am I stupid?
GCC gives a warning, that the template is private....?
What am I missing?
#include <iostream>
using namespace std;
class A
{
private:
template<typename T>
A& operator<<(const T & v)
{
cout << v << endl;
return *this;
}
};
int main()
{
A a;
a << 4;
system("pause");
}
Microsoft acknowledges the bug and claims it will be fixed in the next major release for the compiler (which I read as VC11/VS-whatever-is-after-2010 - probably not a service pack for VC10/VS2010):
http://connect.microsoft.com/VisualStudio/feedback/details/649496/visual-c-doesnt-respect-the-access-modifier-for-operator-member-function-templates
from the comments, the fix appears to be already made to an internal compiler build.
This code should not compile - this is a bug (or silly extension) in VS. GCC should refuse it as well. The operator is inaccessible in the scope it is used.
Comeau treats this correctly:
"ComeauTest.c", line 28: error: function "A::operator<<(const T &) [with T=int]"
(declared at line 14) is inaccessible
a << 4;
EDIT: A relevant standard snippet, from 13.3/1
[Note: the function selected by
overload resolution is not guaranteed
to be appropriate for the context.
Other restrictions, such as the
accessibility of the function, can
make its use in the calling context
ill-formed. ]
No, you're not stupid - it's broken code and should be rejected. The Comeau compiler (http://www.comeaucomputing.com/tryitout) does correctly reject it.