Here is a problem I stumbled accross when refactoring some code and I was wondering if there is a better way to do it:
#include <iostream>
template<typename T>
class Foo
{
public:
Foo()
{
init(x);
}
T x;
};
void init(int& i)
{
i = 42;
}
int main()
{
Foo<int> foo;
std::cout << foo.x << std::endl;
return 0;
}
Unfortunately, this doesn't compile - neither with GCC or with Clang. The function init called in Foo's constructor is not declared. In this toy example, this could be solved by moving the function itself ahead of the template. However, in a more complex context, this may not work. Originally, I intended to use overloads of init to allow some setup for the classes used in the template.
I assumee that in this context init is a non-dependend name - even though the argument of the function call depends on the template parameter (which was odd for me at first). Is there a way to trick it to also consider function definitions defined after the template itself?
I know that I can use template specialization (which was in the original code in the first place, but I wanted to replace it with simpler overloads):
template<typename>
struct Initializer;
template<>
struct Initializer<int>
{
static void init(int& i)
{
i = 42;
}
}
Is there a way to make this work with function overloads as well? I know, boost::serialization also relies on function overloads for custom types, but I did not really find where and how they implemented that.
You can solve the ordering problem by calling through a template function object - in the same way that boost::hash finds the ADL-function hash_value(x).
This works because the expansion of the template is deferred until the point of first use:
#include <iostream>
namespace A {
struct XX {
friend void init(XX&);
};
}
namespace B {
struct YY {
friend void init(YY&);
};
}
/// default case - call init on T found by ADL
template<class T>
struct call_init
{
void operator()(T& o) const {
init(o);
}
};
template<typename T>
class Foo
{
public:
Foo()
{
auto initialiser = call_init<decltype(this->x)>();
initialiser(this->x);
}
T x;
};
void init(int& x) {
x = 2;
}
// special case, initialise an int
template<> struct call_init<int>
{
void operator()(int& x) const {
init(x);
}
};
int main()
{
Foo<int> foo;
Foo<A::XX> foox;
Foo<B::YY> fooy;
std::cout << foo.x << std::endl;
return 0;
}
Your problem is also that intis not a class and no ADL is done for it, replacing int by custom class works:
template<typename T>
class Foo
{
public:
Foo() { init(x); }
T x;
};
struct C
{
int i = 0;
};
void init(C& c) { c.i = 42; }
Demo.
To allow to works for primitive type as int, you have to introduce some custom type:
template <typename> struct tag {};
template<typename T>
class Foo
{
public:
Foo() { init(x, tag<T>{}); }
T x;
};
void init(int& i, tag<int>) { i = 42; }
Demo
Related
I've got an abstract class that uses variable template.
template <class T>
class Abstract
{
public:
virtual void print(T t) = 0;
};
There can be any derivatives of the class like so:
class A : public Abstract<std::string>
{
public:
void print(std::string str)
{
std::cout << str << std::endl;
}
};
class B : public Abstract<int>
{
public:
void print(int number)
{
std::cout << std::to_string(number) << std::endl;
}
};
Now I want a function to return one of these derivatives so I can execute the print method. And here is my Problem:
template (class T); // error here
Abstract<T> &f(int n) // what should the return type look like?
{
if (n == 0)
{
A a{};
return a;
}
else
{
B b{};
return b;
}
}
int main()
{
A a{f(0)};
a.print("foo");
B b{f(1)};
b.print(42);
return 0;
}
So how is it be possible to return a class with unknown parameter type and call its methods?
I already tried returning derived classes without templates which works fine. As soon as templates are added code wont compile. I also tried void* and reinterpret_cast. Problem here is that I have manually to decide to what type to cast to.
So how can I return an arbitrary superclass of an abstract generic class and call its generic methods?
I think inheritance is the wrong approach here. Instead I would use specialization instead:
template<typename T>
struct Foo;
template<>
struct Foo<std::string>
{
void print(std::string const& s)
{
std::cout << s << '\n';
}
};
template<>
struct Foo<int>
{
void print(int value)
{
std::cout << value << '\n';
}
};
Then you don't need a selector to pick the object to create, just the correct type:
int main()
{
Foo<std::string> f1;
f1.print("hello");
Foo<int> f2;
f2.print(123);
}
If you really need a factor function, then it could be created like this:
template<typename T>
Foo<T> create()
{
return Foo<T>();
}
And use like
int main()
{
auto f1 = create<std::string>();
f1.print("hello");
auto f2 = create<int>();
f2.print(123);
}
I want to have a templated friend function. However, I do not know how to make it works in the same way for no templated function.
Here is a sample code
#include <iostream>
namespace ns{
struct Obj {
friend void foo(Obj){std::cout << "no problem" << std::endl;}
template<typename T>
friend void bar(Obj){std::cout << "problem" << std::endl;}
};
}
int main() {
ns::Obj obj;
foo(obj); // Compile
bar<int>(obj); // Not compile
return 0;
}
Before C++20, you need to teach the compiler that bar is the name of a template so that it knows that < starts a template argument list and is not the less-than operator:
template<char> void bar() = delete;
int main() {
ns::Obj obj;
foo(obj); // Compile
bar<int>(obj); // Now compiles too
return 0;
}
Note that all the bar overload has to do is to be a function template. The signature doesn't matter as long as it's not so good as to interfere with the overload resolution; () is a good choice because by definition we are passing at least one argument, so a function template taking no parameters can never be viable.
Alternatively, you can redesign bar to deduce T from a tag argument:
template<class T>
struct type {};
namespace ns{
struct Obj {
// ...
template<typename T>
friend void bar(Obj, type<T>) { /* ... */ }
};
}
// ...
bar(obj, type<int>()); // OK
In C++20, the compiler will assume that bar names a template when it sees the < and name lookup finds nothing, so your code will just work.
The straightforward approach would be to add a forward declaration and let the function be located using qualified lookup:
namespace ns{
struct Obj;
void foo(Obj);
template<typename T>
void bar(Obj);
struct Obj {
friend void foo(Obj){std::cout << "no problem" << std::endl;}
template<typename T>
friend void bar(Obj){std::cout << "problem " << std::endl;}
};
} // namespace ns
int main() {
ns::Obj obj;
ns::foo(obj); // Ok
ns::bar<int>(obj); // Ok
return 0;
}
I have a template class C<T> which I intend to instantiate with T as some other classes A and B. C<T> has a method foo whose signature I would like to depend on whether T was instantiated as A or B. For example, consider the following code:
#include <iostream>
#include <string>
class A {
public:
void message() {
std::cout << "message with no args" << std::endl;
}
};
class B {
public:
void message(int x) {
std::cout << "message with " << x << std::endl;
}
};
template<typename T>
class C {
private:
T internal;
public:
C(T& x) {
internal = x;
}
void call() {
internal.message();
}
void call(int x) {
internal.message(x);
}
};
int main(int argc, char* argv[]) {
A a;
B b;
C<A> ca(a);
C<B> cb(b);
ca.call();
cb.call(42);
// ca.call(42); ERROR HERE
return 0;
}
This runs correctly. ca.call(42) would raise a compilation error because there is no method A::message(int). However, if I for some reason introduce a method A::message(int) in A, the code may allow calling ca.call(42), which I would like to prevent.
I know that SFINAE techniques would allow to declare a method C::call(T::call_type x) where T::call_type would be a typedef for each intended instantiation of T. However, this only allows me to change the type of the argument of C::call. I would like instead to make the signature (in particular, the number of parameters) of C::call on T. I would thus prevent ca.call(42) from being a valid call even if there is a method A::message(int) in A.
Is there any way to do this?
I don't know all the ins and outs of SFINAE, but what do you think of this?
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, A>::value>>
void call() {
internal.message();
}
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, B>::value>>
void call(int x) {
internal.message(x);
}
You can use == false too
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, B>::value == false>>
You can do this with template specializations:
// Main template used by every other type T.
template<typename T>
class C; // or some other implementation.
// This gets used for T = A.
template<>
class C<A> {
private:
A internal;
public:
C(A& x) {
internal = x;
}
void call() {
internal.message();
}
};
// This gets used for T = B.
template<>
class C<B> {
private:
B internal;
public:
C(B& x) {
internal = x;
}
void call(int x) {
internal.message(x);
}
};
If you don't like that you need to duplicate some common code, then you can have a base class with all those common things and inherit from it in each specialization.
How can I do something like this? I want to create a object of class C and use parameters. To elaborate, the error here is the compiler reads this as a conversion, instead of me creating an object with parameters.
EDIT: for those who still don't understand, foobar is irrelevant. I've removed it as the error still occurs without the function.
// define foobar else where
template <class C>
class Dummy {
void foo(int bar) {
C dumdum = C(bar); // Error - '<function-style-cast>': cannot convert from initializer-list to 'C'
}
}
How does that help me?
You can make foo a function template that accepts a parameter pack to make it general.
Example program:
#include <iostream>
#include <sstream>
#include <string>
template <class C>
class Dummy {
public:
template <typename... Args>
void foo(Args... args ) {
foobar(C(args...));
}
};
struct Foo
{
Foo(int, int) {}
};
struct Bar
{
Bar(int) {}
};
struct Baz
{
};
void foobar(Foo)
{
std::cout << "In foobar(Foo)\n";
}
void foobar(Bar)
{
std::cout << "In foobar(Bar)\n";
}
void foobar(Baz)
{
std::cout << "In foobar(Baz)\n";
}
int main()
{
Dummy<Foo>().foo(10, 20);
Dummy<Bar>().foo(10);
Dummy<Baz>().foo();
}
Output:
In foobar(Foo)
In foobar(Bar)
In foobar(Baz)
Have you tried something like:
C dumdum(bar);
Or:
C dumdum{bar};
?
class C {
public:
C(int a) {}
};
template <class C>
class Dummy {
public:
void foo(int bar) {
C dumdum = C(bar);
}
};
int main() {
Dummy<C> dummy;
dummy.foo(2);
return 0;
}
I didn't see any errors.
I am using C++ and templates - but I need to allow using a member function for a specific type and prevent other types from using this function.
For example: I want this class to have print() for all types but have foo() for just type int. How can I do that ?
#include<iostream>
template <class type>
class example
{
private:
type data;
public:
void print(); // for all types
void foo(); // only for 'type' == int?
};
Factor the common, generic functionality into a base class. Specializations can inherit that.
namespace detail {
template <class type>
class example_base
{
private:
type data ;
public:
void print();
};
} // end namespace detail
template <class type>
struct example
: detail::example_base<type> {
using detail::example_base<type>::example_base; // inherit any constructors
};
template <> // specialize the class
struct example< int >
: detail::example_base<int> {
using detail::example_base<int>::example_base; // inherit any constructors
void other_function(); // extend the basic functionality
};
You can specify the template for some types. See this example :
template <typename T>
class MyClass
{
private:
double my_function() { return 1.0; }
};
template <>
class MyClass<double>
{
public:
double my_function() { return 2.0; }
};
int main()
{
MyClass<double> a;
MyClass<int> b;
double x = a.my_function(); // this works
// double y = b.my_function(); // not allowed
return 0;
}
Tested with GCC 4.7.2.
You could use std::enable_if and do something like this to obtain exactly what you want:
#include <iostream>
#include <type_traits>
template<class T>
class example
{
private:
T data;
public:
void print()
{
std::cout << " hello " << std::endl;
}
template<class U = T // make the function templated on U, but with default type T
, typename std::enable_if<std::is_integral<U>::value>::type* = nullptr // enable only for when U (T) is an integral type
>
void foo()
{
std::cout << " I will only compile when T is an integral type " << std::endl;
}
};
int main()
{
example<int> integer_example;
integer_example.print();
integer_example.foo(); // <--- compiles fine as int is integral
example<double> double_example;
double_example.print();
//double_example.foo(); // <--- will not compile
return 0;
}
In the std::enable_if you can also put std::is_same<U,int>::value instead of std::is_integral<U>::value to only allow the function only to be used for int and not other integral types.