I don’t understand why if constexpr works like that [duplicate] - c++

I tried to play with the C++17 standard. I tried to use one of the features of C++17 if constexpr. And I had a problem... Please take a look at the following code. This compiles without errors. In the following code, I tried to use if constexpr to check if it is a pointer.
#include <iostream>
#include <type_traits>
template <typename T>
void print(T value)
{
if constexpr (std::is_pointer_v<decltype(value)>)
std::cout << "Ptr to " << *value << std::endl; // Ok
else
std::cout << "Ref to " << value << std::endl;
}
int main()
{
auto n = 1000;
print(n);
print(&n);
}
But when I rewrite the above code, as shown below, where if constexpr is in the main function:
#include <iostream>
#include <type_traits>
int main()
{
auto value = 100;
if constexpr (std::is_pointer_v<decltype(value)>)
std::cout << "Ptr to " << *value << std::endl; // Error
else
std::cout << "Ref to " << value << std::endl;
}
I get a compilation error:
main.cpp:8:32: error: invalid type argument of unary ‘*’ (have ‘int’)
std::cout << "Ptr to " << *value << std::endl;
Problem is not in the main function. This can be any function similar to the following.
void print()
{
auto value = 100;
if constexpr (std::is_pointer_v<decltype(value)>)
std::cout << "Ptr to " << *value << std::endl; // Error
else
std::cout << "Ref to " << value << std::endl;
}
int main()
{
print();
}
I would like to know why if constexpr works only in template functions, even if the type is deduced by the decltype from the input parameter.

I would like to know why "if constexpr" works only in template functions, even if the type is deduced by the decltype from the input parameter.
This is by design.
if constexpr will not instantiate the branch not taken if it's within a template. It won't just treat the branch not taken as token soup and avoid parsing it or performing semantic analysis entirely. Both sides are still going to be analyzed, and since *value is ill-formed for ints, that's an error.
You simply can't use if constexpr to avoid compiling non-template code. It's only to avoid instantiating template code that's potentially invalid-for-the-particular-specialization.

C++ standard, clause 9.4.1:
If the if statement is of the form if constexpr, the value of the condition shall be a contextually converted constant expression of type bool (8.6); this form is called a constexpr if statement. If the value of the converted
condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity (Clause 17), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.
(emphasis mine)
So, a substatement of a constexpr if still gets instantiated if it is not inside a template, thus it must at least compile.

I would like to know why if constexpr works only in template functions, even if the type is deduced by the decltype from the input parameter.
The thing is, it also work in non template, just not in the way you would expect.
For if constexpr to work like you stated, you not only need a template, but you need the contained expressions to be dependent on the template parameters.
Let's go step by step why this is made that way in C++, and what are the implications.
Let's start simple. Does the following code compile?
void func_a() {
nonexistant();
}
I think we will all agree that it won't compile, we are trying to use a function that hasn't been declared.
Let's add one layer.
Does the following code compile?
template<typename T>
void func_b_1() {
nonexistant();
}
With a correct compiler, this will not compile.
But why is that? You could argue that this code is never actually compiled, since the template is never instantiated.
The standard define something they call two phase name lookup. This is that even if the template is not instantiated, the compiler must perform name lookup an anything that don't depend on the template parameter.
And that make sense. If the expression nonexistant() don't depend on T, why would its meaning change with T? Hence, this expression is the same as in func_a in the eye of the compiler.
So how about dependent names?
template<typename T>
void func_b_2() {
T::nonexistant();
}
This will compile! Why is that? Nowhere in this code there's a function called nonexistant. Yet, you feed that into a compiler as the whole codebase and it will gladly accept it.
And the standard even says that it has to accept it. This is since there could be a T containing nonexistant somewhere. So if you instantiate the template with a type that has the static member function nonexistant it will compile and call the function. If you instantiate the template with a type that don't have the function, it won't compile.
As you can see, the name lookup is done during instantiation. This is called second phase name lookup. The second phase name lookup is done only during instantiation.
Now, enter if constexpr.
To make such construct working well with the rest of the language properly, it has been decided that if constexpr is defined as a branch for instantiation. As such, we can make some code non-instantiated, even in non templates!
extern int a;
void helper_1(int*);
void func_c() {
if constexpr (false) {
helper_1(&a);
}
}
The answer is that helper_1 and a are not ODR used. We could leave helper_1 and a not defined and there would not be linker errors.
Even better, the compiler won't instantiate templates that are in a discarded branch of a if constexpr:
template<typename T>
void helper_2() {
T::nonexistant();
}
void func_d() {
if constexpr (false) {
helper_2<int>();
}
}
This code won't compile with a normal if.
As you can see, the discarded branch of a if constexpr work just like a template that hasn't been instantiated, even in non template code.
Now let's mix it up:
template<typename T>
void func_b_3() {
if constexpr (false) {
nonexistant();
}
}
This is just like our template function in the beginning. We said that even if the template was not instantiated, the code was invalid, since the invalid expression don't depend on T. We also said that if constexpr is a branch in the instantiation process. The error happen before instantiation. This code won't compile either.
So finally, this code won't compile either:
void func_e() {
if constexpr (false) {
nonexistant();
}
}
Even though the content of the if constexpr is not instantiated, the error happen because the fist name lookup step is done, and the error happen before the instantiation process. It is just that in this case, there is no instantiation, but it doesn't matter at this point.
So what are the uses of if constexpr? Why does it seem to work only in templates?
The thing is, it doesn't work differently in templates. Just as we saw with func_b_3, the error still happen.
But, this case will work:
template<typename T>
void helper_3() {
if constexpr (false) {
T::nonexistant();
}
}
void func_f() {
helper_3<int>();
}
The expression int::nonexistant() is invalid, but the code compile. This is because since T::nonexistant() is an expression that depends on T, name lookup is done in the second phase. The second phase of name lookup is done during template instantiation. The if constexpr branch that contain T::nonexistant() is always the discarded part so the second phase of name lookup is never done.
There you go. if constexpr is not about not compiling a portion of code. Just like template, they are compiled and any expression that name lookup can be done is done. if constexpr is about controlling instantiation, even in non template function. All rules that applies to templates also applies to all branch of the if constexpr. Two phase name lookup still applies and allow programmers to not instantiate some part of the code that would otherwise not compile if instantiated.
So if a code cannot compiled in a template that is not instantiated, it won't compile in the branch of the if constexpr that is not instantiated.

Outside a template, a discarded statement is fully checked. if constexpr is not a substitute for the #if preprocessing directive.
here

I am a little late to the party but a nice trick I use when I need if constexpr in a non template function is to wrap my code in a lambda.
struct Empty
{
};
void foo()
{
/* foo is not template, will not compile
if constexpr ( false )
{
std::cout << Empty{}.bar; // does not exist
}
*/
// now the code is in a lambda with auto param, so basicly a template method
[&](const auto& empty)
{
if constexpr ( false )
{
std::cout << empty.bar;
}
}(Empty{});
}
demo : https://wandbox.org/permlink/XEgZ6PXPLyyjXDlO
The fact I can use the [&] syntax make this solution way simpler to use than create an helper method since I don't need to forward all the parameters I need inside my if constexpr

Related

In what sense is std::disjunction short-circuiting at compile_time

From the description on cppreference.com, I was under the impression that std::disjunction is designed to give me short-circuiting at compile-time, so that I can use it like this:
#include <type_traits>
#include <iostream>
template<nullptr_t null = nullptr>
constexpr bool does_not_compile() {
static_assert(null != nullptr);
return false;
}
void hello_world () {
if constexpr (std::disjunction_v<std::true_type, std::bool_constant<does_not_compile()>>) {
std::cout << "Hello World!" << std::endl;
}
}
However, this does not compile, std::disjunction does not short-circuit in the sense that the above static_assert doesn't trigger (live example).
But then it what sense is it short-circuiting? It can't be the usual behavior of || at run time, because the type of the std::disjunction has to be known at compile time, and it depends on its value.
You find an explanation right on the page you linked to:
Disjunction is short-circuiting: if there is a template type argument Bi with bool(Bi::value) != false, then instantiating disjunction<B1, ..., BN>::value does not require the instantiation of Bj::value for j > i
The short-circuiting behavior concerns the value member of each parameter type, not the parameter type itself. You cannot instantiate a template without knowing it's paramters. And using std::disjunction<…> will generally require an instantiation. In your example
std::disjunction_v<std::true_type, std::bool_constant<does_not_compile()>>
the compiler still has to instantiate std::bool_constant<does_not_compile()> so that it knows what the whole std::disjunction<…> comes out to be (as you have noted yourself). What is guaranteed is that it won't instantiate std::bool_constant<does_not_compile()>::value…

Why does "if constexpr" not behave as expected in such a case? [duplicate]

I tried to play with the C++17 standard. I tried to use one of the features of C++17 if constexpr. And I had a problem... Please take a look at the following code. This compiles without errors. In the following code, I tried to use if constexpr to check if it is a pointer.
#include <iostream>
#include <type_traits>
template <typename T>
void print(T value)
{
if constexpr (std::is_pointer_v<decltype(value)>)
std::cout << "Ptr to " << *value << std::endl; // Ok
else
std::cout << "Ref to " << value << std::endl;
}
int main()
{
auto n = 1000;
print(n);
print(&n);
}
But when I rewrite the above code, as shown below, where if constexpr is in the main function:
#include <iostream>
#include <type_traits>
int main()
{
auto value = 100;
if constexpr (std::is_pointer_v<decltype(value)>)
std::cout << "Ptr to " << *value << std::endl; // Error
else
std::cout << "Ref to " << value << std::endl;
}
I get a compilation error:
main.cpp:8:32: error: invalid type argument of unary ‘*’ (have ‘int’)
std::cout << "Ptr to " << *value << std::endl;
Problem is not in the main function. This can be any function similar to the following.
void print()
{
auto value = 100;
if constexpr (std::is_pointer_v<decltype(value)>)
std::cout << "Ptr to " << *value << std::endl; // Error
else
std::cout << "Ref to " << value << std::endl;
}
int main()
{
print();
}
I would like to know why if constexpr works only in template functions, even if the type is deduced by the decltype from the input parameter.
I would like to know why "if constexpr" works only in template functions, even if the type is deduced by the decltype from the input parameter.
This is by design.
if constexpr will not instantiate the branch not taken if it's within a template. It won't just treat the branch not taken as token soup and avoid parsing it or performing semantic analysis entirely. Both sides are still going to be analyzed, and since *value is ill-formed for ints, that's an error.
You simply can't use if constexpr to avoid compiling non-template code. It's only to avoid instantiating template code that's potentially invalid-for-the-particular-specialization.
C++ standard, clause 9.4.1:
If the if statement is of the form if constexpr, the value of the condition shall be a contextually converted constant expression of type bool (8.6); this form is called a constexpr if statement. If the value of the converted
condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity (Clause 17), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.
(emphasis mine)
So, a substatement of a constexpr if still gets instantiated if it is not inside a template, thus it must at least compile.
I would like to know why if constexpr works only in template functions, even if the type is deduced by the decltype from the input parameter.
The thing is, it also work in non template, just not in the way you would expect.
For if constexpr to work like you stated, you not only need a template, but you need the contained expressions to be dependent on the template parameters.
Let's go step by step why this is made that way in C++, and what are the implications.
Let's start simple. Does the following code compile?
void func_a() {
nonexistant();
}
I think we will all agree that it won't compile, we are trying to use a function that hasn't been declared.
Let's add one layer.
Does the following code compile?
template<typename T>
void func_b_1() {
nonexistant();
}
With a correct compiler, this will not compile.
But why is that? You could argue that this code is never actually compiled, since the template is never instantiated.
The standard define something they call two phase name lookup. This is that even if the template is not instantiated, the compiler must perform name lookup an anything that don't depend on the template parameter.
And that make sense. If the expression nonexistant() don't depend on T, why would its meaning change with T? Hence, this expression is the same as in func_a in the eye of the compiler.
So how about dependent names?
template<typename T>
void func_b_2() {
T::nonexistant();
}
This will compile! Why is that? Nowhere in this code there's a function called nonexistant. Yet, you feed that into a compiler as the whole codebase and it will gladly accept it.
And the standard even says that it has to accept it. This is since there could be a T containing nonexistant somewhere. So if you instantiate the template with a type that has the static member function nonexistant it will compile and call the function. If you instantiate the template with a type that don't have the function, it won't compile.
As you can see, the name lookup is done during instantiation. This is called second phase name lookup. The second phase name lookup is done only during instantiation.
Now, enter if constexpr.
To make such construct working well with the rest of the language properly, it has been decided that if constexpr is defined as a branch for instantiation. As such, we can make some code non-instantiated, even in non templates!
extern int a;
void helper_1(int*);
void func_c() {
if constexpr (false) {
helper_1(&a);
}
}
The answer is that helper_1 and a are not ODR used. We could leave helper_1 and a not defined and there would not be linker errors.
Even better, the compiler won't instantiate templates that are in a discarded branch of a if constexpr:
template<typename T>
void helper_2() {
T::nonexistant();
}
void func_d() {
if constexpr (false) {
helper_2<int>();
}
}
This code won't compile with a normal if.
As you can see, the discarded branch of a if constexpr work just like a template that hasn't been instantiated, even in non template code.
Now let's mix it up:
template<typename T>
void func_b_3() {
if constexpr (false) {
nonexistant();
}
}
This is just like our template function in the beginning. We said that even if the template was not instantiated, the code was invalid, since the invalid expression don't depend on T. We also said that if constexpr is a branch in the instantiation process. The error happen before instantiation. This code won't compile either.
So finally, this code won't compile either:
void func_e() {
if constexpr (false) {
nonexistant();
}
}
Even though the content of the if constexpr is not instantiated, the error happen because the fist name lookup step is done, and the error happen before the instantiation process. It is just that in this case, there is no instantiation, but it doesn't matter at this point.
So what are the uses of if constexpr? Why does it seem to work only in templates?
The thing is, it doesn't work differently in templates. Just as we saw with func_b_3, the error still happen.
But, this case will work:
template<typename T>
void helper_3() {
if constexpr (false) {
T::nonexistant();
}
}
void func_f() {
helper_3<int>();
}
The expression int::nonexistant() is invalid, but the code compile. This is because since T::nonexistant() is an expression that depends on T, name lookup is done in the second phase. The second phase of name lookup is done during template instantiation. The if constexpr branch that contain T::nonexistant() is always the discarded part so the second phase of name lookup is never done.
There you go. if constexpr is not about not compiling a portion of code. Just like template, they are compiled and any expression that name lookup can be done is done. if constexpr is about controlling instantiation, even in non template function. All rules that applies to templates also applies to all branch of the if constexpr. Two phase name lookup still applies and allow programmers to not instantiate some part of the code that would otherwise not compile if instantiated.
So if a code cannot compiled in a template that is not instantiated, it won't compile in the branch of the if constexpr that is not instantiated.
Outside a template, a discarded statement is fully checked. if constexpr is not a substitute for the #if preprocessing directive.
here
I am a little late to the party but a nice trick I use when I need if constexpr in a non template function is to wrap my code in a lambda.
struct Empty
{
};
void foo()
{
/* foo is not template, will not compile
if constexpr ( false )
{
std::cout << Empty{}.bar; // does not exist
}
*/
// now the code is in a lambda with auto param, so basicly a template method
[&](const auto& empty)
{
if constexpr ( false )
{
std::cout << empty.bar;
}
}(Empty{});
}
demo : https://wandbox.org/permlink/XEgZ6PXPLyyjXDlO
The fact I can use the [&] syntax make this solution way simpler to use than create an helper method since I don't need to forward all the parameters I need inside my if constexpr

"If constexpr" in C++17 does not work in a non-templated function

I tried to play with the C++17 standard. I tried to use one of the features of C++17 if constexpr. And I had a problem... Please take a look at the following code. This compiles without errors. In the following code, I tried to use if constexpr to check if it is a pointer.
#include <iostream>
#include <type_traits>
template <typename T>
void print(T value)
{
if constexpr (std::is_pointer_v<decltype(value)>)
std::cout << "Ptr to " << *value << std::endl; // Ok
else
std::cout << "Ref to " << value << std::endl;
}
int main()
{
auto n = 1000;
print(n);
print(&n);
}
But when I rewrite the above code, as shown below, where if constexpr is in the main function:
#include <iostream>
#include <type_traits>
int main()
{
auto value = 100;
if constexpr (std::is_pointer_v<decltype(value)>)
std::cout << "Ptr to " << *value << std::endl; // Error
else
std::cout << "Ref to " << value << std::endl;
}
I get a compilation error:
main.cpp:8:32: error: invalid type argument of unary ‘*’ (have ‘int’)
std::cout << "Ptr to " << *value << std::endl;
Problem is not in the main function. This can be any function similar to the following.
void print()
{
auto value = 100;
if constexpr (std::is_pointer_v<decltype(value)>)
std::cout << "Ptr to " << *value << std::endl; // Error
else
std::cout << "Ref to " << value << std::endl;
}
int main()
{
print();
}
I would like to know why if constexpr works only in template functions, even if the type is deduced by the decltype from the input parameter.
I would like to know why "if constexpr" works only in template functions, even if the type is deduced by the decltype from the input parameter.
This is by design.
if constexpr will not instantiate the branch not taken if it's within a template. It won't just treat the branch not taken as token soup and avoid parsing it or performing semantic analysis entirely. Both sides are still going to be analyzed, and since *value is ill-formed for ints, that's an error.
You simply can't use if constexpr to avoid compiling non-template code. It's only to avoid instantiating template code that's potentially invalid-for-the-particular-specialization.
C++ standard, clause 9.4.1:
If the if statement is of the form if constexpr, the value of the condition shall be a contextually converted constant expression of type bool (8.6); this form is called a constexpr if statement. If the value of the converted
condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity (Clause 17), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.
(emphasis mine)
So, a substatement of a constexpr if still gets instantiated if it is not inside a template, thus it must at least compile.
I would like to know why if constexpr works only in template functions, even if the type is deduced by the decltype from the input parameter.
The thing is, it also work in non template, just not in the way you would expect.
For if constexpr to work like you stated, you not only need a template, but you need the contained expressions to be dependent on the template parameters.
Let's go step by step why this is made that way in C++, and what are the implications.
Let's start simple. Does the following code compile?
void func_a() {
nonexistant();
}
I think we will all agree that it won't compile, we are trying to use a function that hasn't been declared.
Let's add one layer.
Does the following code compile?
template<typename T>
void func_b_1() {
nonexistant();
}
With a correct compiler, this will not compile.
But why is that? You could argue that this code is never actually compiled, since the template is never instantiated.
The standard define something they call two phase name lookup. This is that even if the template is not instantiated, the compiler must perform name lookup an anything that don't depend on the template parameter.
And that make sense. If the expression nonexistant() don't depend on T, why would its meaning change with T? Hence, this expression is the same as in func_a in the eye of the compiler.
So how about dependent names?
template<typename T>
void func_b_2() {
T::nonexistant();
}
This will compile! Why is that? Nowhere in this code there's a function called nonexistant. Yet, you feed that into a compiler as the whole codebase and it will gladly accept it.
And the standard even says that it has to accept it. This is since there could be a T containing nonexistant somewhere. So if you instantiate the template with a type that has the static member function nonexistant it will compile and call the function. If you instantiate the template with a type that don't have the function, it won't compile.
As you can see, the name lookup is done during instantiation. This is called second phase name lookup. The second phase name lookup is done only during instantiation.
Now, enter if constexpr.
To make such construct working well with the rest of the language properly, it has been decided that if constexpr is defined as a branch for instantiation. As such, we can make some code non-instantiated, even in non templates!
extern int a;
void helper_1(int*);
void func_c() {
if constexpr (false) {
helper_1(&a);
}
}
The answer is that helper_1 and a are not ODR used. We could leave helper_1 and a not defined and there would not be linker errors.
Even better, the compiler won't instantiate templates that are in a discarded branch of a if constexpr:
template<typename T>
void helper_2() {
T::nonexistant();
}
void func_d() {
if constexpr (false) {
helper_2<int>();
}
}
This code won't compile with a normal if.
As you can see, the discarded branch of a if constexpr work just like a template that hasn't been instantiated, even in non template code.
Now let's mix it up:
template<typename T>
void func_b_3() {
if constexpr (false) {
nonexistant();
}
}
This is just like our template function in the beginning. We said that even if the template was not instantiated, the code was invalid, since the invalid expression don't depend on T. We also said that if constexpr is a branch in the instantiation process. The error happen before instantiation. This code won't compile either.
So finally, this code won't compile either:
void func_e() {
if constexpr (false) {
nonexistant();
}
}
Even though the content of the if constexpr is not instantiated, the error happen because the fist name lookup step is done, and the error happen before the instantiation process. It is just that in this case, there is no instantiation, but it doesn't matter at this point.
So what are the uses of if constexpr? Why does it seem to work only in templates?
The thing is, it doesn't work differently in templates. Just as we saw with func_b_3, the error still happen.
But, this case will work:
template<typename T>
void helper_3() {
if constexpr (false) {
T::nonexistant();
}
}
void func_f() {
helper_3<int>();
}
The expression int::nonexistant() is invalid, but the code compile. This is because since T::nonexistant() is an expression that depends on T, name lookup is done in the second phase. The second phase of name lookup is done during template instantiation. The if constexpr branch that contain T::nonexistant() is always the discarded part so the second phase of name lookup is never done.
There you go. if constexpr is not about not compiling a portion of code. Just like template, they are compiled and any expression that name lookup can be done is done. if constexpr is about controlling instantiation, even in non template function. All rules that applies to templates also applies to all branch of the if constexpr. Two phase name lookup still applies and allow programmers to not instantiate some part of the code that would otherwise not compile if instantiated.
So if a code cannot compiled in a template that is not instantiated, it won't compile in the branch of the if constexpr that is not instantiated.
Outside a template, a discarded statement is fully checked. if constexpr is not a substitute for the #if preprocessing directive.
here
I am a little late to the party but a nice trick I use when I need if constexpr in a non template function is to wrap my code in a lambda.
struct Empty
{
};
void foo()
{
/* foo is not template, will not compile
if constexpr ( false )
{
std::cout << Empty{}.bar; // does not exist
}
*/
// now the code is in a lambda with auto param, so basicly a template method
[&](const auto& empty)
{
if constexpr ( false )
{
std::cout << empty.bar;
}
}(Empty{});
}
demo : https://wandbox.org/permlink/XEgZ6PXPLyyjXDlO
The fact I can use the [&] syntax make this solution way simpler to use than create an helper method since I don't need to forward all the parameters I need inside my if constexpr

C(++) type name function/macro

Is there a macro to return (ideally as a const char* or indeed cont char *const) the typename of its parameter or some information on it?
This is at compile time, not runtime, so C++'s typeinfo will not do. I run with -fno-rtti anyway.
There isn't.
C++11 does have the __func__ identifier inside function definitions, which produces a string variable with the function name. Most C++ compilers have variations on this to have "decorated" function names.
Instead of writing
cout<<mysteryMacro(std::string<wchar>);
you could write
cout<<"std::string<wchar>";
The only purpose I see is to get typeinformation inside a template, e.g.:
template<T>
void printT() {
cout << "T is of type"<<mysteryMacro(T);
}
Since otherwise you automatically know the code.
This could be achieved with template specialization.
Disclaimer: this was untested, and is just a hint for what SHOULD work. I dont consider this a good solution. I am sure you can convert this to rather return strings than to print. A little more input on what you need this for would be great.
template<T>
void printT() {
code that generates compile error, so you see that you have an undefined type
}
template<>
void printT<string>() {
cout << "you have a <string>";
}
If you would like to use the template for variables you would hav to rely on automatic template parameter deduction like so:
template<>
void printT<string>(T) {
cout << "you have a <string>";
}
and use it like this
int x;
printT(x);
eventhough I do not know why, since, as stated unless you are in a template you will know the type while writing the code and in templates the type is conveyed in the template parameters and thus knwon (not as string) and you could always write printT<T>()

Omit return type in C++11

I've recently found myself using the following macro with gcc 4.5 in C++11 mode:
#define RETURN(x) -> decltype(x) { return x; }
And writing functions like this:
template <class T>
auto f(T&& x) RETURN (( g(h(std::forward<T>(x))) ))
I've been doing this to avoid the inconvenience having to effectively write the function body twice, and having keep changes in the body and the return type in sync (which in my opinion is a disaster waiting to happen).
The problem is that this technique only works on one line functions. So when I have something like this (convoluted example):
template <class T>
auto f(T&& x) -> ...
{
auto y1 = f(x);
auto y2 = h(y1, g1(x));
auto y3 = h(y1, g2(x));
if (y1) { ++y3; }
return h2(y2, y3);
}
Then I have to put something horrible in the return type.
Furthermore, whenever I update the function, I'll need to change the return type, and if I don't change it correctly, I'll get a compile error if I'm lucky, or a runtime bug in the worse case. Having to copy and paste changes to two locations and keep them in sync I feel is not good practice.
And I can't think of a situation where I'd want an implicit cast on return instead of an explicit cast.
Surely there is a way to ask the compiler to deduce this information. What is the point of the compiler keeping it a secret? I thought C++11 was designed so such duplication would not be required.
It would appear that g++ 4.8 is getting an implementation of auto return type deduction.
The patch was put in by Jason Merrill who is also sending a paper for C++-1Y for the feature. The feature is available with -std=c++1y.
Still playing with it.
The rationale for this behavior is given in the draft, 8.3.5p12:
A trailing-return-type is most useful
for a type that would be more
complicated to specify before the
declarator-id:
template <class T, class U> auto add(T t, U u) -> decltype(t + u);
rather than
template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);
So this is really only meant to simplify the case where referring to the parameter names helps.
If you assume that C++ could always infer the return type of functions from the function body: this is not going to fly. It's a goal of C++ (and C) to allow modularity by separating declaration from implementation, so at the point of the call, you may not have the body of the function available. However, every caller needs to know the parameter types and the return type of every function/method being called.
If you are simply trying to set the return type, make it a template argument. This way you can change everything related to the return type without actually changing the function. You can put a default return type if you want like in this example.
template <class R = int, class T>
R f(T&& x)
{
...
return h2(y2, y3);
}
The code below demonstrates it's effectiveness.
DEMO CODE:
#include <iostream>
#include <iomanip>
template <class T, class S>
T h2(T& x, S& y)
{
return x + y;
}
template <class R = int, class T>
R f(T& x)
{
auto y2 = x;
auto y3 = x;
return h2(y2, y3);
}
int main(int argc, char** argv)
{
int x = 7;
std::string str = "test! ";
auto d = f<double>(x);
auto i = f(x); // use default type (int)
auto s = f<std::string>(str);
std::cout << std::fixed << std::setprecision(4);
std::cout << "double: " << d << std::endl;
std::cout << "int: " << i << std::endl;
std::cout << "string: " << s << std::endl;
return 0;
}
OUTPUT:
double: 14.0000
int: 14
string: test! test!
Unfortunately, the exact functionality you are looking for does not exist (yet) and is not part of the C++0x spec. However, it is possible this may be part of the C++1x spec when it is drafted. until then, stick to templates.
EDIT: oops, I just realized that there's a scoping difference between the trailing-return-type specifier and the return statement. Specifically:
auto f(int a)
{
char r[sizeof(f(a))+1];
return r;
}
Kaboom!
Previous answer:
It's unfortunate that the language does not provide a syntax to have the compiler infer the return type in this case, because it's trivial to show that inference is possible.
Specifically, we are talking about the case where there is exactly one return statement inside the function.
Independent of where in the function that return statement is, or how complex the preceding code is, it should be clear that the following transformation is possible:
return (ugly expression);
into
auto return_value = (ugly expression);
return return_value;
If the compiler can infer the type of return_value (and according to the C++0x rules, it can), then the inferred type of return_value can be chosen as the return type of the function.
It therefore seems to me that a modification to C++0x where the trailing return type specifier should only be required when the multiplicity of return statements is not exactly one would be feasible and solve the problem.
I agree with Yttrill. Return type deduction has already been proved to be a feasible practice in languages like Haskell, and since C++ has already achieved 'auto', it is just one step further to achieve return type deduction. This deduction should happens at the time of specialization, not template definition, since information of the real type supplied to the template is needed. The separate of declaration and definition is no longer a common practice in generic C++, because template body must be written in header files, and hence template bodies almost always go with template declarations. In situations where there are multiple return statements and types of them do not match, the compiler can happily report en error. In summary, return type deduction is totally possible in C++, if the committee wants to. And it's VERY important, because the duplication of manually writing return types hinders the pervasive use of small generic helper functions, which is such a common practice in functional and generic programming.
Many programming languages including Ocaml and Felix can deduce the return type of a function and do not require it to be specified. In Ocaml you can and must specify it in an interface. In Felix I have found for some functions it is wise to specify it in library code to make it easier to use.
I am surprised "auto" doesn't work for return types, it was certainly on the plate. Was this considered too hard to implement? [It isn't trivial given that a function can be recursive].
Ohhh .. now I see. The problem is the stupid template design feature "dependent names":
template<class T> auto f(T a) { return a.f(); }
So whilst it is a bit tricky to compute the return type of an ordinary function due to overloading, it is impossible with templates due to dependent name lookup: it could only be done after instantiation. But overloading must happen before that. So auto return types can't go in the language because it doesn't generalise to templates.