Pass a reference to a base class as template parameter - c++

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.

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

Partial specialization or instantiation of template class method

I have template struct with several template parameters
template<class Result, class T, class K>
struct MyClass
{
public:
Result foo()
{
return Result{};
}
};
This struct works fine for all templates except the case when Result is void.
I understand, that Result{} cannot be implemented to void type, so my current solution is to use partial specialization like this:
template<class T, class K>
struct MyClass<void, T, K>
{
public:
void foo()
{
return;
}
};
This allows to do following:
int main()
{
MyClass<void, double, char> mycl1;
MyClass<int, double, char> mycl2;
mycl1.foo();
mycl2.foo();
}
Is there a way to make mycl1.foo() compile without partial class specialization in C++ 14 standart? I could use if constexr and type trait is_void_v combination, but I want to find if there is a way to:
specialization of template class method partial explicit
instantiation of template class method
While you cannot do
Result foo()
{
return Result{};
}
If Result is void, you can use
Result foo()
{
return Result();
}
The behavior in this case is the same and you will get a value initialized object returned. This syntax is allowed when Result is void by [expr.type.conv]\2
If the initializer is a parenthesized single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression. If the type is cv void and the initializer is (), the expression is a prvalue of the specified type that performs no initialization. Otherwise, the expression is a prvalue of the specified type whose result object is direct-initialized with the initializer. For an expression of the form T(), T shall not be an array type.
Coming soon though you will be able to use
return Result{};
even if Result is void as C++20 added to that section that {} will work as well for void. [expr.type.conv]\2 now states
If the initializer is a parenthesized single expression, the type conversion expression is equivalent to the corresponding cast expression. Otherwise, if the type is cv void and the initializer is () or {} (after pack expansion, if any), the expression is a prvalue of the specified type that performs no initialization. Otherwise, the expression is a prvalue of the specified type whose result object is direct-initialized with the initializer. If the initializer is a parenthesized optional expression-list, the specified type shall not be an array type.

incomplete class usage with auto in template class

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.

Can an lvalue reference non-type template parameter be inferred?

I have the following code, which I cannot get to work:
struct foo {};
foo foo1 = {};
template <foo& F>
class FooClass {};
template <foo& F>
void foobar(FooClass<F> arg) {
}
int main() {
FooClass<foo1> f;
foobar(f);
}
The error is:
main.cpp:14:5: error: no matching function for call to 'foobar'
note: candidate template ignored: substitution failure : deduced non-type template argument does not have the same type as the its corresponding template parameter ('foo' vs 'foo &')
Is it at all possible to infer lvalue reference template parameters? If so, how should it be done?
This is precisely covered by CWG 2091:
According to 14.8.2.5 [temp.deduct.type] paragraph 17,
If P has a form that contains <i>, and if the type of the corresponding value of A differs from the type of i, deduction
fails.
This gives the wrong result for an example like:
template<int &> struct X;
template<int &N> void f(X<N>&);
int n;
void g(X<n> &x) { f(x); }
Here, P is X<N>, which contains <i>. The type of i is int&.
The corresponding value from A is n, which is a glvalue of type
int. Presumably this should be valid.
I think this rule means to say something like,
If P has a form that contains <i>, and the type of i differs from the type of the corresponding template parameter of the template
named by the enclosing simple-template-id, deduction fails.
As noted by #dyp, [temp.deduct.type]/17 should be more permissive. In your example, the argument in FooClass<F> (F) does not have reference type - it's an lvalue of type foo. The template parameter of FooClass is a reference. The DR was resolved last year.

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&.