sizeof and function template: sizeof(&f) vs sizeof(&f<int>) - c++

As mentioned by [5.3.3/3] (expr.sizeof, working draft):
The sizeof operator can be applied to a pointer to a function, but shall not be applied directly to a function.
The following minimal, working example compiles fine:
void f() { }
int main() {
sizeof(&f);
}
I would expect that also the one below would work:
template<typename T>
void f() { }
int main() {
sizeof(&f<int>);
}
Anyway, even if it compiles with clang (v3.8), it does not using GCC (v6.1).
The error is:
error: address of overloaded function with no contextual type information
I suspect it's a bug of GCC (I will open a ticket if confirmed).
Am I right or I'm missing something here and GCC is right indeed?
Meanwhile, I opened an issue to GCC.

This is a bug. The following code compiles fine:
template<typename T>
void f() { }
int main() {
auto fptr = &f<int>;
return sizeof(fptr);
}
Note that at first I didn't read the question attentively. I was under the impression that the function f<int> is indeed overloaded, for example as below:
template<typename T>
void f() { }
template<typename T>
void f(T) { }
int main() {
sizeof(&f<int>);
}
Under such reading of the question I prepared the following answer, which I still want to share with the community:
I wouldn't qualify it as a bug.
The argument for qualifying it as a bug is the following - all function pointers have the same size, so why does it matter which function is meant inside the sizeof operator?
I am going to defeat that argument.
First of all, it starts from the wrong premise. The C++ standard only guarantees that
converting a prvalue of type “pointer to T1” to the type “pointer to
T2” (where T1 and T2 are function types) and back to its original type yields the original pointer value
which doesn't necessarily mean that the size of pointers to functions of different types is the same. I admit, however, that in practice this is true.
Next, even if we accept the premise and logic behind the argument, then we must also accept the claim that the following program should also compile without any problem:
template<typename T>
void f() { }
template<typename T>
void f(T) { }
int main() {
auto fptr = &f<int>;
return sizeof(fptr);
// fptr is not used anywhere else, so the compiler must not
// whine about the ambiguity on its declaration line
}
Continuing in this manner, we would argue that compilation ambiguities should never be reported provided that they are eliminated by subsequent code.

Related

Why does C++ lambda overloading not behave as expected?

#include <type_traits>
template<typename T, typename... Args_>
concept IsCallable = std::is_invocable_v<T, Args_...>;
struct A
{
int n = 0;
void f(IsCallable<int&> auto const fn)
{
fn(n);
}
void f(IsCallable<int const&> auto const fn) const
{
fn(n);
}
};
struct Lambda
{
void operator()(auto& n) const
{
n = 1;
}
};
int main()
{
auto a = A{};
a.f(Lambda{}); // ok
a.f([](auto& n) { n = 1; }); // error: assignment of read-only reference 'n'
}
See online demo
Why does C++ lambda overloading not behave as expected?
Your question is nearly a duplicate of Hard error when using std::invoke_result_t with a generic lambda . As with the other question, your code will work the way you expect, if you change the lambda so that it has an explicit -> void return type, thus avoiding return type deduction.
The difference between your question and the other one is that in your question, you're using std::invocable_v rather than std::invoke_result_t. However, the code in this case is still ill-formed because return type deduction is still triggered even though you're not asking for it.
The standard requires is_invocable to determine whether or not the INVOKE expression would be "well-formed when treated as an unevaluated operand". libstdc++ and libc++ both use decltype to create an unevaluated operand for this purpose, so, obviously, return type deduction has to be done. But it seems to me that there is no way to avoid triggering return type deduction, therefore there is no possible standard library implementation that would allow your code to compile (it is not a bug in GCC/Clang).

Result of decltype in const methods

The C++11 decltype returns the type of the expression given to it (mostly). But this can differ from the type of the expression as it is actually accessible:
template<typename T>
struct Ref {
Ref(T&) { }
};
#define GETTYPE decltype
//#define GETTYPE typeof
struct Problem {
void doit_c() const { Ref<GETTYPE(n)> rn{n}; }
void doit_nc() { Ref<GETTYPE(n)> rn{n}; }
int n;
};
int main() {
int i;
const int ci = 0;
Problem pr;
// decltype == typeof == int
Ref<GETTYPE(i)> ri{i};
pr.doit_nc();
// decltype == typeof == const int
Ref<GETTYPE(ci)> rci{ci};
Ref<GETTYPE(static_cast<const int&>(i))> rcci{static_cast<const int&>(i)};
// typeof == const int, decltype == int (!)
pr.doit_c();
return 0;
}
In the example, the Ref struct is just used to cause a compile error if T does not match the actual constructor argument. The Problem::doit_c() method is where decltype(n) returns a non-const result, even though n is const in this context.
If one switches from the standard decltype to the GNU extension typeof, this seems to take the const-ness of the method into account.
Now my question: Is there a C++11 / C++14 / C++17 compliant alternative to decltype() / typeof() that behaves "correctly" (as in: no compile error above) for expressions like the declaration in the const-method above?
Edited:
Simplified the first sentence to remove some errors and stop distracting from the point of the question (thanks, #skypjack)
Simplified the use use of macros in the example code (thanks, #Richard Critten)
decltype is a feature that kinda sits at two chairs at once. Firstly, as the name suggests, it can give you the exact declared type of an entity, ignoring the context in which it is used. Secondly, it can treat its argument as an expression, whose exact type depends on the context and its value category.
decltype applied directly to a "naked" (unparenthesized) class member access is a special case, in which decltype acts in accordance with its first role. It will not treat n as an expression. Instead it will produce the type of that class member, ignoring the context.
If you want it to treat n as an expression, you have to parenthesize it
struct Problem {
void doit_c() const
{
Ref<decltype(n)> rn1{n}; // `decltype(n)` is `int` -> ERROR
Ref<decltype((n))> rn2{n}; // `decltype((n))` is `const int &` -> compiles OK
}
};
You can find out the effective cv-qualified type of n using a temporary reference:
void doit_c() const { auto& x = n; Ref<GETTYPE(x)> rn{n}; }
void doit_nc() { auto& x = n; Ref<GETTYPE(x)> rn{n}; }
But it's simpler and clearer to parenthesize, as shown in AnT's answer.

Brace-initialization vs. Parenthesis Bug

I was filing a GCC bug for this, but I'd rather double-check this.
Consider the following programs:
#include <utility>
template<typename T, typename A>
void F(A&& a) { T(std::forward<A>(a)); } // Note: () syntax.
int main() { int i; F<int&>(i); }
and:
#include <utility>
template<typename T, typename A>
void F(A&& a) { T{std::forward<A>(a)}; } // Note: {} syntax.
int main() { int i; F<int&>(i); }
Latest Clang and MSVC compilers accept both programs. GCC 5 and beyond accept the first program but reject the second, claiming invalid cast of an rvalue expression of type 'int' to type 'int&'.
Is this a GCC bug? Or is this indeed a difference between T{} and T() in the above context (and thus a bug in Clang and MSVC)?
Edit:
The issue can be narrowed down to the following simpler excerpts:
int i; (int&){i};
and
int i; (int&)(i);
There are two separate issues:
The standard is unclear what T{x} should do for reference type T. Currently [expr.type.conv]/1 says that it creates a prvalue of type T, which is nonsense for reference types. This is core issue 1521.
The sane thing is probably to have T{x} for reference type T do roughly T __tmp{x}; and then yield the equivalent of static_cast<T>(__tmp) (so xvalue for rvalue reference T and lvalue for lvalue reference T). However, C++11 as published screwed up the specification for list-initialization of references, making it always create a temporary. The result was that int i; int &r{i}; failed to compile because it would attempt to bind r to a temporary copy of i, which is obviously nonsense. This is fixed by core issue 1288, whose resolution GCC is supposed to implement, but it looks like from the error message that it's not completely fixed.

Why is my constructor with non const reference as argument allowed to be called with temporary objects?

I have a sample code below.
#include<iostream>
template<typename T>
class XYZ
{
private:
T & ref;
public:
XYZ(T & arg):ref(arg)
{
}
};
class temp
{
int x;
public:
temp():x(34)
{
}
};
template<typename T>
void fun(T & arg)
{
}
int main()
{
XYZ<temp> abc(temp());
fun(temp()); //This is a compilation error in gcc while the above code is perfectly valid.
}
In the above code even though XYZ constructor takes argument as non const reference, it compiles fine while the fun function fails to compile. Is this specific to g++ compiler or c++ standard has to say something about it?
Edit:
g++ -v gives this.
gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4)
XYZ<temp> abc(temp());
It compiles, because it is NOT a variable declaration. I'm sure you think its a variable declaration when the fact is that its a function declaration. The name of the function is abc; the function returns an object of type XYZ<temp> and takes a single (unnamed) argument which in turn is a function returning type temp and taking no argument. See these topics for detail explanation:
The Most Vexing Parse (at InformIT)
Most vexing parse (at wikipedia)
And fun(temp()) doesn't compile, because temp() creates a temporary object and a temporary object cannot be bound to non-const reference.
So the fix is this : define your function template as:
template<typename T>
void fun(const T & arg) //note the `const`
{
}
No, the standard doesn't allow to pass a temporary to non const reference. (C++0X introduced rvalue reference to allow this in some controlled cases), see 8.5.3/5 (which is too long for me to cite, the meaningful part is otherwise, the reference shall be to a non-volatile const type, but you have to read the whole list of cases to know that they don't apply here).
And
XYZ<temp> abc(temp());
is simply yet another example of the most vexing parse.

C++0x decltype fails to deduce member variable constness

Consider the following code:
template <typename T>
class B
{
};
template <typename T>
B<T> f(T& t)
{
return B<T>();
}
class A
{
class C {};
C c;
public:
A() {}
decltype(f(c)) get_c() const { return f(c); }
};
int main()
{
A a;
a.get_c();
}
When I try to compile this, I get the error:
test.cpp: In member function 'B<A::C> A::get_c() const':
test.cpp:31:46: error: conversion from 'B<const A::C>' to non-scalar type 'B<A::C>' requested
It seems that in the decltype, the compiler doesn't know that this is a const member function and therefore c is of type const C, and as a result incorrectly deduces the type of f(c) to be B<C> rather than B<const C> which is what it really is.
Am I doing something incorrectly, or is this a compiler bug? I use gcc 4.6, but 4.4 and 4.5 exhibit the same behaviour.
The compiler operates correctly according to the current C++0x WP. See this issue report, which is currently being worked on.
Possibly the final C++0x Standard won't change the meaning of your decltype application in the return type before the function name. You would need to move it to after the parameter list using -> decltype(f(c)), which hopefully will do The Right thing in final C++0x.
No, decltype is not supposed to take into account whether the function is const or not, because it can't. The same could have been written differently:
typedef decltype(f(c)) return_type;
return_type get_c() const { return f(c); }
Correction: decltype(f(c)) shouldn't even compile, because c is not static.
f needs to take an rvalue reference, not an lvalue reference.
I don't think you're allowed to use decltype on anything you wouldn't normally be able to call. I haven't been able to find anything in the standard that would allow you to access c, even within a decltype expression, outside of anywhere you could use c. Since you don't have a this pointer at the point you're trying to do your thing, I don't think you can do what you're trying to do. Doing so doesn't work in MSVC 2010 at least, and it has nothing to do with const.
I considered using declval to get one but you can't access A&&.c because A is an incomplete type at that point. I can't see anyway to do what you're trying to do other than something like so:
decltype(f(declval<C const>())) get_c() const { ... }