incomplete class usage with auto in template class - c++

Is the following code well formed ?
class B;
template<class T>
class A
{
B do_f() const;
friend auto f(A const& a) {return a.do_f();} // #1
};
class B{};
template <class T>
B A<T>::do_f() const { return B{};}
int main()
{
A<double> a;
f(a);
}
If I change auto in #1 by B, I got incomplete type error message.
Compile with auto for gcc/clang Demo
Fail with B Demo

[dcl.fct.def.general]/2:
The type of a parameter or the return type for a function definition shall not be an incomplete or abstract (possibly cv-qualified) class type in the context of the function definition unless the function is deleted ([dcl.fct.def.delete]).
But [dcl.spec.auto]/10:
Return type deduction for a function template with a placeholder in its declared type occurs when the definition is instantiated even if the function body contains a return statement with a non-type-dependent operand.
So with B, it's ill-formed by the first rule. But with auto, deduction doesn't take place until the function is instantiated... by which point the type is complete, so it's fine.
Note that the first rule only applies to the definition, which is why do_f() is okay. You can have declarations which return incomplete types.
The above wording technically doesn't apply to this case. We don't have a function template. But the intent is for it to apply to any kind of templated thing. There's a PR to editorially fix this from:
Return type deduction for a function template with a placeholder [...]
To:
Return type deduction for a templated entity that is a function or function template with a placeholder in its
Which does apply here.

Related

Template argument deduction from inherited type

I want a setup like the following:
template <typename T> class a {};
class b : public a<int> {};
template <typename T>
void do_foo(std::unique_ptr<a<T>> foo)
{
// Do something with foo
}
int main()
{
do_foo(std::make_unique<b>());
}
This fails to compile with a note saying template argument deduction/substitution failed and mismatched types 'a<T>' and 'b'. It's pretty self-explanatory. I can help the compiler along by writing do_foo<int>(std::make_unique<b>());, but then I'm repeating myself by writing int twice.
Is there a way to get the compiler to deduce the template parameter in this case? And what would you call this behaviour? I tried searching for things like "template type deduction for inherited type", "polymorphic template deduction" etc.
Is there a way to get the compiler to deduce the template parameter in this case?
No. Not in C++14 (or even C++20).
And what would you call this behaviour?
Standard compliant. To be specific, this paragraph applies:
[temp.deduct.call]
4 In general, the deduction process attempts to find template
argument values that will make the deduced A identical to A (after
the type A is transformed as described above). However, there are
three cases that allow a difference:
If the original P is a reference type, the deduced A (i.e., the type referred to by the reference) can be more cv-qualified than the
transformed A.
The transformed A can be another pointer or pointer to member type that can be converted to the deduced A via a qualification
conversion ([conv.qual]).
If P is a class and P has the form simple-template-id, then the transformed A can be a derived class of the deduced A.
Likewise, if P is a pointer to a class of the form
simple-template-id, the transformed A can be a pointer to a derived class pointed to by the deduced A.
This is an exhaustive list of cases where a template argument can be validly deduced from a function argument even if it doesn't match the pattern of the function parameter exactly. The first and second bullets deal with things like
template<class A1> void func(A1&){}
template<class A2> void func(A2*){}
int main() {
const int i = 1;
func(i); // A1 = const int
func(&i); // A2 = const int
}
The third bullet is the one that is closest to our case. A class derived from a template specialization can be used to deduce a template parameter pertaining to its base. Why doesn't it work in your case? Because the function template parameter is unique_ptr<a<T>> and the argument you call it with is unique_ptr<b>. The unique_ptr specializations are not themselves related by inheritance. So they don't match the bullet, and deduction fails.
But it doesn't mean that a wrapper like unique_ptr prevents template argument deduction entirely. For instance:
template <typename> struct A {};
struct B : A<int> {};
template<typename> struct wrapper{};
template<> struct wrapper<B> : wrapper<A<int>> {};
template<typename T>
void do_smth(wrapper<A<T>>) {}
int main() {
do_smth(wrapper<B>{});
}
In this case, wrapper<B> derives from wrapper<A<int>>. So the third bullet is applicable. And by the complex (and recursive) process of template argument deduction, it allows B to match A<T> and deduce T = int.
TL;DR: unique_ptr<T> specializations cannot replicate the behavior of raw pointers. They don't inherit from the specializations of unique_ptr over T's bases. Maybe if reflection ever comes to C++, we'll be able to meta-program a smart pointer that does behave that way.
As workaround, you might add overload:
template <typename T>
void do_foo_impl(a<T>* foo)
{
return do_foo(std::unique_ptr<a<T>>(foo));
}
template <typename T>
auto do_foo(std::unique_ptr<T> foo) -> decltype(do_foo_impl(foo.release()))
{
do_foo_impl(foo.release());
}
Demo

Pass a reference to a base class as template parameter

I have the following code where I try to specialize a function template for each instance of a class (that may have been derived):
class Base {
};
class Derived:public Base {
};
template<Base& b>
void myfunction() {
//use b somehow
}
Derived myobject;
int main() {
myfunction<myobject>(); //this does not work
}
The code results in error message:
candidate template ignored: invalid explicitly-specified argument for template parameter 'b'
[live demo]
How do I pass a reference to a static instance of type Base, given the static Derived object myobject?
While it is fine to declare a template non-type parameter as a reference according to [temp.param]/4:
A non-type template-parameter shall have one of the following
(optionally cv-qualified) types:
...
lvalue reference to object or lvalue reference to function,
...
The argument must follow the restrictions in [temp.arg.nontype]/2:
A template-argument for a non-type template-parameter shall be a
converted constant expression of the type of the template-parameter.
For a non-type template-parameter of reference or pointer type, the
value of the constant expression shall not refer to (or for a pointer
type, shall not be the address of):
a subobject,
...
Which explicitly forbids what you are trying to do. Since b is going to end up referring to a sub-object.
The only solution which will make this compile, is adding another overload:
template<Derived & d>
void myfunction()
{
//use d somehow
}
So you'll need to extract the common code out somehow.
Or, if you have C++17 available:
template<auto& b, std::enable_if_t<
std::is_base_of_v<Base, std::decay_t<decltype(b)>>
, void*> = nullptr>
void myfunction()
{
//use b somehow
}
I suggest you re-think your general approach, however.

Is a class declaration without definition valid as template argument

Is it valid to use an identifier of a class, that is only declared but not defined, as template argument and for template specialization.
Something like that:
template<typename T>
class NodeInfo;
template<typename T>
class GraphInfo;
template<typename T>
class Graph {
public:
GraphInfo<T> graphInfo;
NodeInfo<T> nodeInfo;
};
// specialisation
class ContextInfo;
template <>
class NodeInfo<ContextInfo> {
public:
int a, b, c;
};
template <>
class GraphInfo<ContextInfo> {
public:
int a, b, c;
};
int main() {
Graph<ContextInfo> g;
}
This compiles fine without any warning in gcc 7, but I'm wondering if this is a valid thing to do or do I create some kind of undefined behavior with that?
This is fine in principle:
[ Note: A template type argument may be an incomplete type (6.9). — end note ]
However, incomplete class types may not be used with the standard library, unless otherwise specified:
[The] effects are undefined in the following cases: [...]
— if an incomplete type (6.9) is used as a template argument when instantiating a template component,
unless specifically allowed for that component.
For example:
The template parameter T of declval may be an incomplete type.
Since C++17, more library facilities permit the use of incomplete types; for example vector:
An incomplete type T may be used when instantiating vector if the allocator satisfies the allocator completeness
requirements. T shall be complete before any member of the resulting specialization of
vector is referenced.
Yes it's pretty fine. An incomplete type is allowed to used as template argument.
A template argument for a type template parameter must be a type-id, which may name an incomplete type

Friend function template with automatic return type deduction cannot access a private member

Sorry for how complicated the title of this question is; I tried to describe the minimal SSCCE I constructed for this problem.
I have the following code:
#include <iostream>
namespace fizz
{
template<typename... Ts>
class bar
{
public:
template<int I, typename... Us>
friend auto foo(const bar<Us...> &);
private:
int i = 123;
};
template<int I, typename... Ts>
auto foo(const bar<Ts...> & b)
{
return b.i;
}
}
int main()
{
std::cout << fizz::foo<1>(fizz::bar<int, float>{});
}
This code compiles with GCC 5.2 and doesn't with Clang 3.7:
main.cpp:19:18: error: 'i' is a private member of 'fizz::bar<int, float>'
return b.i;
^
main.cpp:25:24: note: in instantiation of function template specialization 'fizz::foo<1, int, float>' requested here
std::cout << fizz::foo<1>(fizz::bar<int, float>{});
^
main.cpp:13:13: note: declared private here
int i = 123;
^
However, if you change the code slightly (although in a way that is not exactly useful for me, since in the real code this would introduce tons of boilerplate):
#include <iostream>
namespace fizz
{
template<typename... Ts>
class bar
{
public:
template<int I, typename... Us>
friend int foo(const bar<Us...> &);
private:
int i = 123;
};
template<int I, typename... Ts>
int foo(const bar<Ts...> & b)
{
return b.i;
}
}
int main()
{
std::cout << fizz::foo<1>(fizz::bar<int, float>{});
}
it suddenly works with that Clang 3.7.
The difference is that in the version of the code that doesn't compile with Clang, the friend function template uses C++14 auto return type deduction, while the working one plainly says it returns int. The same problem also happens with other variants of auto return type deduction, like auto && or const auto &.
Which compiler is right? Please provide some standard quotes to support the answer, since it is quite possible that a bug will need to be filed for one (...hopefully not both) compilers... or a standard defect, if both are right (which wouldn't be the first time).
I believe it's a clang bug. I want to approach it from this direction. What wrinkles does the auto placeholder type add, as compared to having a specified return type? From [dcl.spec.auto]:
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 trailing-return-type specifies the declared return type
of the function. Otherwise, the function declarator shall declare a 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.
auto can appear in foo's declaration and definition, and is valid.
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 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 ]
The first time we need to use determine the type of an expression, the return type of the function will already have been deduced from the return in the definition of foo(), so this is still valid.
Redeclarations or specializations of a function or function template with a declared return type that uses a
placeholder type shall also use that placeholder, not a deduced type.
We're using auto in both places, so we don't violate this rule either.
In short, there are several things that differentiate a specific return type from an placeholder return type from a function declaration. But all the usages of auto in the example are correct, so the namespace-scope foo should be seen as a redeclaration and definition of the first-declared friend auto foo within class template bar. The fact that clang accepts the former as a redeclaration for return type int but not for auto, and there is no relevant different for auto, definitely suggests this is a bug.
Further, if you drop the int I template parameter so that you can call foo unqualified, clang will report the call as ambiguous:
std::cout << foo(fizz::bar<int, float>{});
main.cpp:26:18: error: call to 'foo' is ambiguous
std::cout << foo(fizz::bar<int, float>{});
^~~
main.cpp:10:21: note: candidate function [with Us = <int, float>]
friend auto foo(const bar<Us...> &);
^
main.cpp:17:10: note: candidate function [with Ts = <int, float>]
auto foo(const bar<Ts...>& b)
^
So we have two function templates named foo in the same namespace (since from [namespace.memdef] the friend declaration for foo will place it in the nearest enclosing namespace) that take the same arguments and have the same return type (auto)? That shouldn't be possible.
It appears that your first example should work. There is a statement in C++14 (7.1.6.4 p12):
Redeclarations or specializations of a function or function template with a declared return type that uses a
placeholder type shall also use that placeholder, not a deduced type. [ Example:
.
.
.
template <typename T> struct A {
friend T frf(T);
};
auto frf(int i) { return i; } // not a friend of A<int>
The reason for the example appears to be to explain that to make the declarations match (and cause the defined function to be a friend) the declaration of frf inside struct A would also need to use auto. This implies to me that having a friend declaration with an auto return type and later defining the friend function (and also using auto) is allowed. I can't find anything that would make this work differently for a member function template, like in your example.

dynamic cast a reference and auto

I've encountered a pretty weird behavior when using auto and dynamic_cast.
This is the class hierachy i have:
class BaseInterface {
public:
virtual void someMethod()=0;
};
class Derived:public BaseInterface {
public:
virtual void someMethod1()=0;
void someMethod()override;
};
And of course there are some classes that implement all derived methods.
Then there is a third class which looks like this:
class ThirdClass {
public:
void demoMethod(BaseInterface&);
void anotherMethod(Derived&);
};
void ThirdClass::demoMethod(BaseInterface& obj) {
auto buffer=dynamic_cast<Derived&>(obj);
anotherMethod(buffer);
}
When i compile this with gcc i get an "cannot allocate an object of abstract type" error. Whereas when i replace
auto buffer=...
with
Derived& buffer=...
everything compiles fine. Why is that the case? Is auto not deducing the right type or something?
Also i found a dirty trick to still use auto:
void ThirdClass::demoMethod(Base& obj) {
auto buffer=dynamic_cast<Derived*>(&obj);
anotherMethod(*buffer);
}
You're getting Derived from auto. Use this instead:
auto & buffer = dynamic_cast<Derived&>(obj);
§7.1.6.4/7:
When a variable declared using a placeholder type is initialized […]
the deduced return type or variable type is determined from the type
of its initializer. […] let T be the declared type of the variable
or return type of the function. If the placeholder is the auto
type-specifier, the deduced type is determined using the rules for
template argument deduction. […] obtain P from T by replacing the
occurrences of auto with either a new invented type template
parameter U[…]. Deduce a value for U using the rules of template
argument deduction from a function call (14.8.2.1), where P is a
function template parameter type and the corresponding argument is the
initializer.
So, in order to familiarize yourself with the process, take a look at the actual rule used for deducing the type of buffer: What happens if you change
template <typename U>
void f( U );
to
void f( Derived& );
when calling f with an lvalue of type Derived? Clearly, for the function template, U will be deduced as Derived, which then yields a deduction failure.
This directly corresponds to the deduction of the placeholder type in your example - auto will be replaced by Derived, and that fails, as Derived is abstract.
Generally speaking, if you write
auto obj = …;
obj will never be a reference, just as U will never be deduced as a reference type when calling the above function template.
Instead, use auto&:
auto& buffer = dynamic_cast<Derived&>(obj);
Now, P is U&:
template <typename U>
void f(U&);
U is, of course, still deduced as Derived, but the type of P - which is effectively the type of buffer - is Derived&.