Use of 'auto func(int)' before deduction of 'auto' in C++14
This is [dcl.spec.auto/11]:
If the type of an entity with an undeduced placeholder type is needed
to determine the type of an expression, the program is ill-formed.
Once a non-discarded return statement has been seen in a function,
however, the return type deduced from that statement can be used in
the rest of the function, including in other return statements.
[ Example:auto n = n; // error, n's type is unknown
auto f();
void g() { &f; } // error, f's return type is unknown
auto sum(int i) {
if (i == 1)
return i; // sum's return type is int
else
return sum(i-1)+i; // OK, sum's return type has been deduced
}
— end example ]
To translate this into English: the compiler needs to know the return type before you can use the function. In case of auto
used like this, this is typically achieved by moving the definition before the point of use. If you don't actually need to use return type deduction, you can keep the definition after the use if you provide a signature, including the return type, in the declaration.
use of decltype(auto) func before deduction of auto
Every source file (.cpp
file or .cc
file) has to stand on its own. The standard calls these files translation units, because the behaviour of the program is the same as-if all those files are translated into machine language as separate units, and only then the machine code is linked into one program.
Standing "on its own" means that the source file together with all files included in it have to convey all information to translate the source file. If you put the masterMethod
in one source file and main
in a different source file, the compiler has no idea which type is returned by masterMethod
when compiling main.
The answer to your question
How would one seperate implementation and declaration as I would like to do it?
Is thus: Either you put the source code of the function in the header file, or you give up on using return type deduction. If you put the source code into the header file, you don't need to put it inside the class definition, as long as you declare it inline.
Use of 'auto [...] 'before deduction of 'auto' with recursive, concept-based function template
There are two problems here.
The first problem is yours:
namespace rng {
template <std::ranges::range Rng>
auto deep_flatten(Rng&& rng) {
using namespace std::ranges;
if constexpr (range<Rng>) { // <==
return deep_flatten(rng | views::join);
} else {
return rng | views::join;
}
}
}
This function is infinitely recursive. deep_flatten
is constrained range<Rng>
, so the if constexpr
check there is always going to be true, so we're never going to enter the base case. This is just a bug - we're checking the wrong thing, it's not if we're a range, it's if our underlying value is a range. That's:
namespace rng {
template <typename Rng>
auto deep_flatten(Rng&& rng) {
using namespace std::ranges;
auto joined = rng | views::join;
if constexpr (range<range_value_t<decltype(joined)>>) {
return deep_flatten(joined);
} else {
return joined;
}
}
}
And here we get into the second problem, which is the standard library's problem. What rng | views::join
means is:
The name
views::join
denotes a range adaptor object ([range.adaptor.object]). Given a subexpressionE
, the expressionviews::join(E)
is expression-equivalent tojoin_view{E}
.
But join_view{E}
for an E
that's already a specialization of join_view
... is a no-op right now because of class template argument deduction (CTAD) - the copy deduction candidate is the best candidate, so our nested join
operation actually becomes a single join
. Your original implementation gets around this problem because it's not join
-ing a join_view
, it's always join
-ing vector
s.
I've submitted LWG 3474.
In the meantime, we can work around the views::join
problem by just directly using join_view
and specifying the template argument explicitly:
namespace rng {
template <typename Rng>
auto deep_flatten(Rng&& rng) {
using namespace std::ranges;
auto joined = join_view<views::all_t<Rng>>(rng);
if constexpr (range<range_value_t<decltype(joined)>>) {
return deep_flatten(joined);
} else {
return joined;
}
}
}
This works.
How do I deduce auto before a function is called?
No, there is not.
Even ignoring the practical problems (requiring multi-pass compilation, ease of making undecidable return types via mutually recursive type definitions, difficulty in isolating source of compilation errors when everything resolves, etc), and the design issues (that forward declaration is nearly useless), C++11 was designed with ease of implementation in mind. Things that made it harder to write a compiler needed strong justification.
The myriad restrictions on auto
mean that it was really easy to slide it into existing compilers: it is among the most supported C++11 features in my experience. C++14 relaxes many of the restrictions, but does not go nearly as far as you describe. Each relaxation requires justification and confidence that it will be worth the cost to compiler writers to implement.
I would not even want that feature at this time, as I like the signatures of my functions to be deducible at the point I call them, at the very least.
use of boost::hana::eval_if_t before deduction of auto
First, in case the path doesn't make it clear, boost/hana/fwd/eval_if.hpp
is but a forward declaration. If you want to use it in earnest, you need to include the whole thing, i.e., boost/hana/eval_if.hpp
. That's the cause of the original error.
Then, this bool
is wrong:
constexpr bool b = is_a<std::reference_wrapper<std::string>,
arg_t>;
Coercing it to bool
means that the type no longer carries the value information. Use auto
.
Recursive lambda fails to compile if return type is not specified
When I try to compile this, I get the following message:
main.cpp: In instantiation of 'main()::<lambda(auto:1, int)> [with auto:1 = main()::<lambda(auto:1, int)>]':
main.cpp:10:17: required from here
main.cpp:7:18: error: use of 'main()::<lambda(auto:1, int)> [with auto:1 = main()::<lambda(auto:1, int)>]' before deduction of 'auto'
7 | _func(_func, n - 1);
| ~~~~~^~~~~~~~~~~~~~
The key is
error: use of 'lambda' before deduction of 'auto'
A simpler example would be something like
#include<iostream>
auto f(int x) {
std::cout << x << " ";
if (x > 0)
f(x - 1);
}
int main() {
f(3);
}
which gives much the same error.
In essence, the compiler can't know until its finished processing the function what return type it needs to be, but it can't finish processing the function until it works out what f
is returning. There's a circular dependency here, so the compiler gives an error.
See also Use of 'auto func(int)' before deduction of 'auto' in C++14.
Why can't the type of my class-static auto function be deduced within the class scope?
While the way you have written the code makes it appear possible, the in-class definition of foo()
can only be processed after the class is fully defined. It is as if you wrote this:
struct Foo
{
static auto foo(int bar);
typedef std::result_of<decltype(Foo::foo)> foo_t;
};
auto Foo::foo(int bar)
{
return 0;
}
The definition of foo()
is allowed to use types defined in class Foo
, including foo_t
, which would be circular. Therefore, the definition of class Foo
is not allowed to use the definition of its member functions--only their declarations.
In other words, you assume the code is fully evaluated from the top to the bottom. It is not.
What are some uses of decltype(auto)?
Return type forwarding in generic code
For non-generic code, like the initial example you gave, you can manually select to get a reference as a return type:
auto const& Example(int const& i)
{
return i;
}
but in generic code you want to be able to perfectly forward a return type without knowing whether you are dealing with a reference or a value. decltype(auto)
gives you that ability:
template<class Fun, class... Args>
decltype(auto) Example(Fun fun, Args&&... args)
{
return fun(std::forward<Args>(args)...);
}
Delaying return type deduction in recursive templates
In this Q&A a few days ago, an infinite recursion during template instantiation was encountered when the return type of the template was specified as decltype(iter(Int<i-1>{}))
instead of decltype(auto)
.
template<int i>
struct Int {};
constexpr auto iter(Int<0>) -> Int<0>;
template<int i>
constexpr auto iter(Int<i>) -> decltype(auto)
{ return iter(Int<i-1>{}); }
int main() { decltype(iter(Int<10>{})) a; }
decltype(auto)
is used here to delay the return type deduction after the dust of template instantiation has settled.
Other uses
You can also use decltype(auto)
in other contexts, e.g. the draft Standard N3936 also states
7.1.6.4 auto specifier [dcl.spec.auto]
1 The
auto
anddecltype(auto)
type-specifiers designate a placeholder
type that will be replaced later, either by deduction from an
initializer or by explicit specification with a trailing-return-type.
Theauto
type-specifier is also used to signify that a lambda is a
generic lambda.2 The placeholder type can appear with a function declarator in the decl-specifier-seq, type-specifier-seq,
conversion-function-id, or trailing-return-type, in any context where such a declarator is valid. If the function
declarator includes a trailing-return-type (8.3.5), that specifies the declared return type of the function.
If the declared return type of the function contains a placeholder type, the return type of the function is
deduced from return statements in the body of the function, if any.
The draft also contains this example of variable initialization:
int i;
int&& f();
auto x3a = i; // decltype(x3a) is int
decltype(auto) x3d = i; // decltype(x3d) is int
auto x4a = (i); // decltype(x4a) is int
decltype(auto) x4d = (i); // decltype(x4d) is int&
auto x5a = f(); // decltype(x5a) is int
decltype(auto) x5d = f(); // decltype(x5d) is int&&
auto x6a = { 1, 2 }; // decltype(x6a) is std::initializer_list<int>
decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression
auto *x7a = &i; // decltype(x7a) is int*
decltype(auto)*x7d = &i; // error, declared type is not plain decltype(auto)
Related Topics
Visual Studio 2015 Code Analysis C6386 Warns of Buffer Overrun
Why Is a C++ Bool Var True by Default
C++ Dynamic Array Initialization with Declaration
How to Properly Use System() to Execute a Command in C++
What Are the Advantages and Disadvantages of Implementing Classes in Header Files
How to Specify Vc11 Lambda Calling Convention
Convert Std::Variant to Another Std::Variant with Super-Set of Types
C++ "Hello World" Boost Tee Example Program
What Are Practical Applications of Weak Linking
Why Does Destructor Disable Generation of Implicit Move Methods
How to Implement "_Mm_Storeu_Epi64" Without Aliasing Problems
Why Do I Need to Include Both the iOStream and Fstream Headers to Open a File