Restrict template friends of a class - c++

Consider the following code:
#include <iostream>
class S {
static const int i = 42;
template <class T>
friend void f();
};
template <class T>
void f()
{
std::cout << S::i << "\n";
}
int main()
{
f<int>();
f<double>();
}
All I want here is to allow access to private part of a class S to f<int>, but not for f<double>. I.e. I want to get compiler error like 'i' is a private member of 'S' for f<double>() line.
How to achive this?

The template instantiation is a function, so just name it: void f<int>().
You need a prior declaration, though:
[C++03: 11.4/9 | C++11/C++14: 11.3/11]: If a friend declaration appears in a local class (9.8) and the name specified is an unqualified name, a prior declaration is looked up without considering scopes that are outside the innermost enclosing non-class scope. For a friend function declaration, if there is no prior declaration, the program is ill-formed. [..]
(This wasn't the case for the friend-inside-template-declaration that you had originally, because templates are looked up a little later in the parsing process.)
Here's the finished solution:
#include <iostream>
template <class T>
void f();
class S {
static const int i = 42;
friend void f<int>();
};
template <class T>
void f()
{
std::cout << S::i << "\n";
}
int main()
{
f<int>();
f<double>();
}
Now the only error is caused by the f<double>() call.
(live demo)

You need to declare (or just define) the template function before the class S. Then you can just say that you want it to be friends with f<int>(). Like so:
template <class T> void f();
class S {
static const int i = 42;
friend void f<int>();
};
template <class T>
void f()
{
std::cout << S::i << "\n";
}

Related

template behavior for static templatized member function in the header file only

class Myclass
{
template <typename T>
static T func()
{
T obj;
return obj;
}
template<>
static int func<int>()
{
}
};
I wrote above class and tried to compile it.
I got following error:
error: explicit specialization in non-namespace scope 'class Myclass'
error: template-id 'func' in declaration of primary template
Then I moved out my static function out side the class like this:
namespace helper
{
template <typename T>
static T func()
{
T obj;
return obj;
}
template<>
static int func<int>()
{
}
}
class Myclass
{
template <typename T>
static T func()
{
helper::func<T>();
}
};
I got following error:
error: explicit template specialization cannot have a storage class
In static member function 'static T Myclass::func()':
Then of course I inline my specialized function and it worked.
namespace helper
{
template <typename T>
static T func()
{
T obj;
return obj;
}
template<>
inline int func<int>()
{
return 1;
}
}
class Myclass
{
template <typename T>
static T func()
{
helper::func<T>();
}
};
My questions are:
1) Why can't we specialize static member functions inside the class.
2) Why can't we have static template specialized functions
Honestly, the real answers to both questions are probably "because."
You can specialize a member function template, it just has to be outside of the class and cannot use the static keyword:
struct Myclass {
template <class T>
static T func() {
T obj{};
return obj;
}
};
template <>
int Myclass::func<int>() { return 42; }
Both are mostly grammatical reasons. It's just one of those things that you have to remember.
To my knowledge C++ does not allow member template specialization at class level. The specializations must be provided at namespace level, so you could declare the specialization outside the class:
// myclass.h
class MyClass
{
public:
template <class T>
void test(T )
{
std::cout << "non-specialized test" << std::endl;
}
template <class T>
static T myfunc()
{
std::cout << "non-specialized myfunc" << std::endl;
return T{};
}
};
template <>
inline void MyClass::test(double t)
{
std::cout << "specialized test" << std::endl;
}
template <>
static double MyClass::myfunc();
/*template <>
static inline void MyClass::myfunc(double )
{
std::cout << "specialized myfunc" << std::endl;
}*/
Then you provide the implementation in the source file:
// myclass.cpp
template <>
static double MyClass::myfunc()
{
std::cout << "specialized myfunc" << std::endl;
return 0.0;
}
Alternatively, you could inline myfunc() in the header file (just like test() function).
Regarding your second question, I tried it in VS2015 and it worked. The only problem is that you are missing return values.

Friend explicit specialization of function template and ADL

Why in the following the partial specialization is not selected by ADL?
template<class T>
void func1(T&){ // selected
...
}
namespace first{
template<class R>
struct foo{
friend void func1<>(foo<R>&){ // expected
cout << "foo.func1" <<endl;
}
};
}
foo<int> f;
func1(f);
Template parameters are unrelated with friend declarations. You'll need to carry them disambiguated in thefriend declaration:
template<class R>
struct foo{
template<typename U>
friend void func1<U>(foo<U>&){
cout << "foo.func1" <<endl; // cat();
}
};
Also for your case you should decide, if you want to put the friend definition inlined as above, or just provide a declaration:
template<class R>
struct foo{
template<typename U>
friend void ::func1<U>(foo<U>&);
};
The latter should match the friend template function in the global namespace explicitly, and specialization can be made as necessary:
template<>
void func1(int&){
// ...
}
template<>
void func1(std::string&){
// ...
}
// a.s.o.
You don't need to provide an specialization of func1. Just provide an overload:
namespace first {
template <class R>
struct foo {
friend void func1(foo& ){
std::cout << "foo.func1" << std::endl;
}
};
}
int i;
first::foo<int> f;
func(i); // calls ::func<int>
func1(f); // calls first::func1(first::foo<int>& );
Otherwise, you can friend a specizliation, but you can't define a specialization in the class body:
template <class R>
struct foo {
friend void func1<>(foo& ); // friends ::func1<foo<R> >
};

explicit instantiation after specialization

#include <iostream>
using namespace std;
template <typename>
class Test
{
void fun() { cout << "test" << endl; }
void bar() { cout << "bar"; }
};
template<>
class Test<int>
{
void fun(){}
};
template void Test<int>::fun();
I got an error:
error: template-id 'fun<>' for 'void Test::fun()' does not match any template declaration
But why?
I know it work if add template for fun in Test e.g.
template<>
class Test<int>
{
template <typename>
void fun(){}
};
template void Test<int>::fun<bool>();
For function template
template<class T> void sort(Array<T>& v) { /*...*/ } // primary template
template<> //explicit specialization of sort(Array<String>)
void sort<String>(Array<String>& v); // after implicit instantiation
template
void sort(Array<String>& v);// no matter before/after void f(Array<String>& v) , it both works
void f(Array<String>& v) {
sort(v); // implicitly instantiates sort(Array<String>&),
} // using the primary template for sort()
An explicit specialisation (that is, not a partial specialisation) is no longer a template. That means all of its members really exist (as if they were instantiated), so you cannot (and need not) instantiate them.

Explicit template specialization cannot have a storage class - member method specialization

Say I have the following code in Visual Studio
class foo
{
public:
template<typename t>
void foo_temp(int a , t s_)
{
std::cout << "This is general tmeplate method";
}
template<>
static void foo_temp(int a , int s)
{
std::cout << "This is a specialized method";
}
};
int main()
{
foo f;
f.foo_temp<std::string>(12,"string");
}
Now I am attempting to covert this into GCC. Going through other questions on SO I noticed that in GCC member methods cannot be specialized if the class is not specialized. I therefore came up with this solution
class foo
{
public:
template<typename t>
void foo_temp(int a , t s_)
{
std::cout << "This is general template method";
}
};
template <>
/*static*/ void foo::foo_temp<int>(int a, int value) {
std::cout << "Hello world";
}
Now this seems to do the trick however when I include the static keyword into the statement i get the error
explicit template specialization cannot have a storage class
Now this thread talks about it but I am still confused on how I could apply that here. Any suggestions on how I can make the last method static ? Also I am still confused as to why templated methods cant be static in GCC ?
This is the visual studio code
class foo
{
public:
template<typename t>
void foo_temp(int a , t s_)
{
std::cout << "This is general tmeplate method";
}
template<>
static void foo_temp(int a , int s)
{
std::cout << "This is a specialized method";
}
};
int main()
{
foo f;
f.foo_temp<std::string>(12,"string");
}
Any suggestions on how I can make the last method static?
You can't; it's unsupported by the language.
Also I am still confused as to why templated methods cant be static in GCC?
They can; they just can't be both static and non-static. Example:
struct foo {
template<typename T>
void bar() {}
template<typename T>
static void baz() {}
};
int main() {
foo f;
f.template bar<void>();
foo::baz<void>();
}
It's very confusing to me why you must have a static specialization of a (non-static) template member function. I would seriously re-evaluate this code for sanity.
Note, to the question in the comments, it is not possible to have a template specialization of a static member function, because it is not possible to have a template specialization of a member function in this situation at all. (Use overloading instead.)
struct foo {
template<typename T, typename U>
static void bar(T, U) {}
// Error, you'd need to also specialize the class, which requires a template class, we don't have one.
// template<>
// static void bar(int, int) {}
// test.cpp:2:12: error: explicit specialization of non-template ‘foo’
// 2 | struct foo {
// | ^
// Partial specializations aren't allowed even in situations where full ones are
// template<typename U>
// static void bar<int, U>(int, U) {}
// test.cpp:14:33: error: non-class, non-variable partial specialization ‘bar<int, U>’ is not allowed
// 14 | static void bar<int, U>(int, U) {}
// | ^
// Instead just overload
template<typename U>
static void bar(int, U) {}
};
Did you try good old fashioned overloading? Don't make the static method a template at all and let overloading priority take care of picking it.
The static method isn't the problem here, the template<> declaration inside a class is the main culprit. You can't declare specialized template inside a class. you can use namespace instead:
namespace foo{
template<typename t>
void foo_temp(int a , t s_)
{
std::cout << "This is general tmeplate method";
}
template<>
void foo_temp(int a , int s)
{
std::cout << "This is a specialized method";
}
}
int main()
{
foo::foo_temp<int>(12,7);
}
Or you can use it inside class like this:
class foo
{
public:
template<typename t>
void foo_temp(int a , t s_)
{
std::cout << "This is general tmeplate method";
}
static void foo_temp(int a , int s)
{
std::cout << "This is a specialized method";
}
};
int main()
{
foo f;
f.foo_temp(12,"string");
f.foo_temp(12,6);
}
N.B: you should call both function (at least the second one) like f.foo_temp(a,b) instead of f.foo_temp<int>() in this case.

Function template specialization format

What is the reason for the second brackets <> in the following function template:
template<> void doh::operator()<>(int i)
This came up in SO question where it was suggested that there are brackets missing after operator(), however I could not find the explanation.
I understand the meaning if it was a type specialization (full specialization) of the form:
template< typename A > struct AA {};
template<> struct AA<int> {}; // hope this is correct, specialize for int
However for function templates:
template< typename A > void f( A );
template< typename A > void f( A* ); // overload of the above for pointers
template<> void f<int>(int); // full specialization for int
Where does this fit into this scenarion?:
template<> void doh::operator()<>(bool b) {}
Example code that seems to work and does not give any warnings/error (gcc 3.3.3 used):
#include <iostream>
using namespace std;
struct doh
{
void operator()(bool b)
{
cout << "operator()(bool b)" << endl;
}
template< typename T > void operator()(T t)
{
cout << "template <typename T> void operator()(T t)" << endl;
}
};
// note can't specialize inline, have to declare outside of the class body
template<> void doh::operator()(int i)
{
cout << "template <> void operator()(int i)" << endl;
}
template<> void doh::operator()(bool b)
{
cout << "template <> void operator()(bool b)" << endl;
}
int main()
{
doh d;
int i;
bool b;
d(b);
d(i);
}
Output:
operator()(bool b)
template <> void operator()(int i)
I've looked it up, and found that it is specified by 14.5.2/2:
A local class shall not have member templates. Access control rules (clause 11) apply to member template names. A destructor shall not be a member template. A normal (non-template) member function with a given name and type and a member function template of the same name, which could be used to generate a specialization of the same type, can both be declared in a class. When both exist, a use of that name and type refers to the non-template member unless an explicit template argument list is supplied.
And it provides an example:
template <class T> struct A {
void f(int);
template <class T2> void f(T2);
};
template <> void A<int>::f(int) { } // non-template member
template <> template <> void A<int>::f<>(int) { } // template member
int main()
{
A<char> ac;
ac.f(1); //non-template
ac.f(’c’); //template
ac.f<>(1); //template
}
Note that in Standard terms, specialization refers to the function you write using an explicit specialization and to the function generated using instantiation, in which case we have to do with a generated specialization. specialization does not only refer to functions you create using explicitly specializing a template, for which it is often only used.
Conclusion: GCC gets it wrong. Comeau, with which i also tested the code, gets it right and issues a diagnostic:
"ComeauTest.c", line 16: error: "void doh::operator()(bool)" is not an entity that
can be explicitly specialized
template<> void doh::operator()(bool i)
Note that it isn't complaining about the specialization of the template for int (only for bool), since it doesn't refer to the same name and type: The function type that specialization would have is void(int), which is distinct from the function type of the non-template member function, which is void(bool).