How to declare member template of class template as friend? - c++

Given the following code:
template <typename T, typename D> class B;
template <typename T>
class A {
public:
A() { }
template <typename D>
A(B<T, D>);
};
template <typename T, typename D>
class B {
friend A<T>::A(B<T, D>);
int x;
};
template <typename T>
template <typename D>
A<T>::A(B<T, D> b) {
b.x = 42;
}
int main() {
B<int, double> b;
A<int> a(b);
return 0;
}
I want to declare member template A(B<T, D>) of class template A<T> as friend.
So I declared, friend A<T>::A(B<T, D>);
But I got a compile error:
test.cc: In instantiation of ‘class B<int, double>’:
test.cc:24:18: required from here
test.cc:13:10: error: prototype for ‘A<int>::A(B<int, double>)’ does not match any in class ‘A<int>’
friend A<T>::A(B<T, D>);
^~~~
test.cc:4:7: error: candidates are: constexpr A<int>::A(A<int>&&)
class A {
^
test.cc:4:7: error: constexpr A<int>::A(const A<int>&)
test.cc:8:3: error: template<class D> A<T>::A(B<T, D>) [with D = D; T = int]
A(B<T, D>);
^
test.cc:6:3: error: A<T>::A() [with T = int]
A() { }
^
test.cc: In instantiation of ‘A<T>::A(B<T, D>) [with D = double; T = int]’:
test.cc:25:13: required from here
test.cc:20:5: error: ‘int B<int, double>::x’ is private within this context
b.x = 42;
~~^
test.cc:14:7: note: declared private here
int x;
^
How to fix it?

The important part of the error message seems to be this line:
test.cc:8:3: error: template<class D> A<T>::A(B<T, D>) [with D = D; T = int]
Notice how the template type D is not expanded to the actual type.
That lead me to believe that adding a new template type for the friend declaration might help:
template<typename U>
friend A<T>::A(B<T, U>);
And it works in my testing.
After thinking a little bit about the reasons behind this, I think it's because there's really no such (constructor) function as A::A(B<T, D>), only the template template<typename D> A::A(B<T, D>).

Related

Order of definition of constrainsts and concepts c++20

Where is the right position of a concept or constraint?
The following code compile:
void f(int x) { }
template <typename T>
concept Concept =
requires (T a)
{ f(a); };
template <typename T>
requires Concept<T>
struct A
{ };
int main(){
A<int> a;
}
but if I change the position of the function f(int) to:
template <typename T>
concept Concept =
requires (T a)
{ f(a); };
void f(int x) { } // <-- new position
template <typename T>
requires Concept<T>
struct A
{ };
int main(){
A<int> a;
}
this doesn't compile in gcc 11.3.0. I get the following error:
main.cpp:27:10: error: template constraint failure for ‘template<class T> requires Concept<T> struct A’
27 | A<int> a;
| ^
main.cpp:27:10: note: constraints not satisfied
main.cpp: In substitution of ‘template<class T> requires Concept<T> struct A [with T = int]’:
main.cpp:27:10: required from here
main.cpp:12:9: required for the satisfaction of ‘Concept<T>’ [with T = int]
main.cpp:13:5: in requirements with ‘T a’ [with T = int]
main.cpp:14:8: note: the required expression ‘f(a)’ is invalid
14 | { f(a); };
If a function is declared after a template, the only way the template can call it is via ADL. int is a built-in type, so ADL doesn't happen for it.
If the parameter was e.g. a class type, defined in the same namespace as f(), it would work.
Clang explains this:
...
<source>:5:7: note: because 'f(a)' would be invalid: call to function 'f' that is neither visible in the template definition nor found by argument-dependent lookup
It works if you predeclare the function. It is not defined at that point in the file.
It's the same principle as when calling functions, you need a predeclaration to be able to use it without seeing the definition.
void f(int x);
template <typename T>
concept Concept =
requires (T a)
{ f(a); };
void f(int x) { } // <-- new position
template <typename T>
requires Concept<T>
struct A
{ };
int main(){
A<int> a;
}

Simple SFINAE Problem conditionally declaring member function

I have read the following threads:
no type named ‘type’ in ‘struct std::enable_if<false, void>
Selecting a member function using different enable_if conditions
"What happened to my SFINAE" redux: conditional template class members?
However, I seem to be unable to make this fairly simple SFINAE problem work on either gcc and msvc:
#include <type_traits>
#include <iostream>
template<typename A, typename B>
class Test {
public:
template<typename X=A, typename = typename std::enable_if<std::is_same<X, void>::value, void>::type >
void foo() {
std::cout << "A";
}
template<typename X=A, typename = typename std::enable_if<!std::is_same<X, void>::value, void>::type >
void foo() {
std::cout << "B";
}
};
int main(int argc, char **argv) {
Test<int, float> t;
t.foo();
return 0;
}
Actual result:
A = void: Full error:
main.cpp:15:8: error: 'template<class A, class B> template<class X, class> void Test<A, B>::foo()' cannot be overloaded with 'template<class A, class B> template<class X, class> void Test<A, B>::foo()'
15 | void foo() {
| ^~~
main.cpp:10:8: note: previous declaration 'template<class A, class B> template<class X, class> void Test<A, B>::foo()'
10 | void foo() {
| ^~~
A = int: Full error:
main.cpp:15:8: error: 'template<class A, class B> template<class X, class> void Test<A, B>::foo()' cannot be overloaded with 'template<class A, class B> template<class X, class> void Test<A, B>::foo()'
15 | void foo() {
| ^~~
main.cpp:10:8: note: previous declaration 'template<class A, class B> template<class X, class> void Test<A, B>::foo()'
10 | void foo() {
| ^~~
main.cpp: In function 'int main(int, char**)':
main.cpp:26:9: error: no matching function for call to 'Test<int, float>::foo()'
26 | t.foo();
| ^
main.cpp:10:8: note: candidate: 'template<class X, class> void Test<A, B>::foo() [with X = X; <template-parameter-2-2> = <template-parameter-1-2>; A = int; B = float]'
10 | void foo() {
| ^~~
main.cpp:10:8: note: template argument deduction/substitution failed:
main.cpp:9:26: error: no type named 'type' in 'struct std::enable_if<false, void>'
9 | template<typename X=A, typename = typename std::enable_if<std::is_same<X, void>::value, void>::type >
| ^~~~~~~~
Expected result
A = void: Outputs "A"
A = int: Outputs "B"
What I want is to implement a different (additional) member function based on a template parameter.
However, it seems like I cannot make the enable_if dependent on the class template types, but I am not sure why. According to the linked threads, the code above appears correct.
Could you please explain why this is not working?
Live Link
The notes from cppreference show similar and explains why it does not work:
A common mistake is to declare two function templates that differ only in their default template arguments. This does not work because the declarations are treated as redeclarations of the same function template (default template arguments are not accounted for in function template equivalence).
/* WRONG */
struct T {
enum { int_t,float_t } m_type;
template <typename Integer,
typename = std::enable_if_t<std::is_integral<Integer>::value>
>
T(Integer) : m_type(int_t) {}
template <typename Floating,
typename = std::enable_if_t<std::is_floating_point<Floating>::value>
>
T(Floating) : m_type(float_t) {} // error: treated as redefinition
};
/* RIGHT */
struct T {
enum { int_t,float_t } m_type;
template <typename Integer,
std::enable_if_t<std::is_integral<Integer>::value, int> = 0
>
T(Integer) : m_type(int_t) {}
template <typename Floating,
std::enable_if_t<std::is_floating_point<Floating>::value, int> = 0
>
T(Floating) : m_type(float_t) {} // OK
};
Applying the same fix to your code, makes it output the desired B :
#include <type_traits>
#include <iostream>
template<typename A, typename B>
class Test {
public:
template<typename X = A,std::enable_if_t<std::is_same<X, void>::value, int> = 0>
void foo() {
std::cout << "A";
}
template<typename X=A,std::enable_if_t<!std::is_same<X, void>::value, int> = 0>
void foo() {
std::cout << "B";
}
};
In your code the two function templates differed only in their default arguments. After the fix the second parameter is either int = 0 or a substition failure.
Here's a C++17 version:
template<typename X=A>
std::enable_if_t<std::is_same_v<X, void>> foo() {
std::cout << "A";
}
template<typename X=A>
std::enable_if_t<!std::is_same_v<X, void>> foo() {
std::cout << "B";
}
(The default type for enable_if is void which is used as the type for the function)
You could also make it with constexpr if:
void foo() {
if constexpr (std::is_same_v<A, void>) {
std::cout << "A";
} else {
std::cout << "B";
}
}
C++11:
template<typename X=A>
typename std::enable_if<std::is_same<X, void>::value>::type foo() {
std::cout << "A";
}
template<typename X=A>
typename std::enable_if<!std::is_same<X, void>::value>::type foo() {
std::cout << "B";
}

Class template argument deduction with partial specialization

I'm having some trouble understanding all the limitations of the new C++17 feature that allows template deduction on constructors.
In particular, this example compiles correctly:
struct B {};
template <typename T, typename = T>
struct A {
A(T) {}
};
int main() {
B b;
A a(b); // ok
}
While this one does not:
struct B {};
template <typename T, typename = T>
struct A;
template <typename T>
struct A<T> {
A(T) {}
};
int main() {
B b;
A a(b); // error
}
The error in this second case is:
main.cpp: In function ‘int main()’:
main.cpp:17:14: error: class template argument deduction failed:
A a(b);
^
main.cpp:17:14: error: no matching function for call to ‘A(B&)’
main.cpp:4:12: note: candidate: template<class T, class> A(A<T, <template-parameter-1-2> >)-> A<T, <template-parameter-1-2> >
struct A;
^
main.cpp:4:12: note: template argument deduction/substitution failed:
main.cpp:17:14: note: ‘B’ is not derived from ‘A<T, <template-parameter-1-2> >’
A a(b);
^
Why is this happening?
Class template argument deduction only considers constructors from the primary class template in order to do deduction. In the first example, we have one constructor that we synthesize a function template for:
template <class T> A<T> __f(T );
The result of __f(b) is A<B>, and we're done.
But in the second example, the primary class template is just:
template <typename T, typename = T>
struct A;
It has no constructors, so we have no function templates to synthesize from them. All we have is a hypothetical default constructor and the copy deduction guide, which together give us this overload set:
template <class T> A<T> __f();
template <class T> A<T> __f(A<T> );
Neither of which are viable for __f(b) (the compile error you get is about trying to match the copy deduction guide), so deduction fails.
If you want this to succeed, you'll have to write a deduction guide:
template <class T>
A(T ) -> A<T>;
Which would allow A a(b) to work.

how to get template parameter of a template template parameter?

Assume std::vector didnt have a value_type. Is is possible to write a template to deduce the value_type? Or more general, given a T<X>, how can I deduce X?
A very naive..
template <template <typename X> T>
void test(T<X> t) {
X x;
}
will probably make anybody who knows a bit about templates laugh at my foolish attempt and when instantiated like this:
int main() {
std::vector<int> x;
test(x);
}
create the following errors:
error: expected ‘class’ before ‘T’
template < template<typename X> T>
^
error: ‘X’ was not declared in this scope
void test(T<X> u) {
^
error: template argument 1 is invalid
void test(T<X> u) {
^
In function ‘void test(int)’:
error: ‘X’ was not declared in this scope
X x;
^
error: expected ‘;’ before ‘x’
X x;
^
In function ‘int main()’:
error: no matching function for call to ‘test(std::vector<int>&)’
test(x);
^
note: candidate is:
note: template<template<class X> class T> void test(int)
void test(T<X> u) {
^
note: template argument deduction/substitution failed:
note: cannot convert ‘x’ (type ‘std::vector<int>’) to type ‘int’
EDIT: the first one is easy to fix, but fixing it wont affect the others...
PS: I think I have a small misunderstanding, as std::vector<int> isnt a template, but a concrete type. However, still I would like to know if there is a way to get the int from a someTemplate<int> with some template magic.
You can create a traits to extract those parameter:
template <typename T> struct first_param;
template <template <typename, typename...> class C, typename T, typename ...Ts>
struct first_param<C<T, Ts...>>
{
using type = T;
};
For pre C++11, you have to handle number of parameters until acceptable values:
template <typename T> struct first_param;
template <template <typename> class C, typename T>
struct first_param<C<T>>
{
typedef T type;
};
template <template <typename, typename> class C, typename T, typename T2>
struct first_param<C<T, T2>>
{
typedef T type;
};
template <template <typename, typename, typename> class C,
typename T, typename T2, typename T3>
struct first_param<C<T, T2, T3>>
{
typedef T type;
};
// ...

"ambiguating new declaration" error for a templated method in a templated class

I have written the following earth-shattering application:
class SomeA { }; class SomeB { }; class SomeC { };
template <typename A, typename B, typename... Cs>
class Foo {
public:
template <typename U> static void bar();
};
template <typename U>
void Foo<SomeA, SomeB, SomeC>::bar() { };
int main() { return 0; }
When I compile this (gcc 4.9.3 with -std=c++11), I get the following error:
a.cpp:10:36: error: ambiguating new declaration of ‘static void Foo<SomeA, SomeB, SomeC>::bar()’
void Foo<SomeA, SomeB, SomeC>::bar() { };
^
a.cpp:6:36: note: old declaration ‘static void Foo<A, B, Cs>::bar() [with U = U; A = SomeA; B = SomeB; Cs = {SomeC}]’
template <typename U> static void bar();
^
Why is this an "ambiguating declaration", and how else can I implement bar for all Us but for a specific instantiation of Foo?
With clang 3.6.2, I get the error:
a.cpp:9:1: error: template parameter list matching the non-templated nested type 'Foo<SomeA, SomeB, SomeC>' should be empty ('template<>')
template <typename U>
^ ~~~~~~~~~~~~
I don't really get this either. How am I supposed to template over U if clang wants an empty parameter list?
No idea what ambiguating new declaration means, but you're specializing the enclosing class template Foo, so you need to indicate that with an empty template parameter list
template <>
template <typename U>
void Foo<SomeA, SomeB, SomeC>::bar() { }
Live demo