c++ assigment operator with two or more template arguments - c++

Code example :
#include <cstdio>
class Test
{
public:
template<typename T, typename T2>
bool operator =(T& value)
{
return true;
}
};
template bool Test::operator= <int, bool>(int&);
int main()
{
Test t;
int gg = 1234;
t = gg;
}
Checked several compilers. Problem with line t=gg.
If i remove second template argument T2, Everything compiles and works. Is it not allowed to have several template argumens for assigment operator?

You may use as many templates parameters as you need.
The problem is that the compiler does not have a means to deduce other template parameters except the template parameters that are specified in the declaration of the operator parameter or that have a default argument.
So you will have to write something like the following
#include <iostream>
class Test
{
public:
template<typename T, typename T2>
bool operator =( T& )
{
return true;
}
};
template bool Test::operator= <int, bool>(int&);
int main()
{
Test t;
int gg = 1234;
t.operator =<int, bool>( gg );
}

Is it not allowed to have several template argumens for assigment operator? [sic]
It's not that it's not allowed, but rather that the compiler has no way to deduce the second argument.
If your function looked something like this:
template<typename T>
bool operator =(T& value)
Then template parameter T could be deduced: It will be the type of whatever you're trying to set t to. Since this is all the template parameters, you're good.
However, what happens when you have 2?
template<typename T, typename T2>
bool operator =(T& value)
T is easily deducible, but what about T2? How would the compiler know what T2 should be? The answer is it can't.
You do have the option of telling it, by calling the operator function directly:
t.operator=<int, bool>(gg);
But I would imagine that wouldn't be what you want.
Unfortunately for you, the following expression won't work:
t =<bool> gg;
So I think calling operator=() directly is your only option here.
Imagine that in operator i create object of type T2 and put it some list. I did not show full source code, because problem can be seen from this part
It sounds like you're using the operator=() for something it wasn't designed to do. operator=() is really only supposed to be used for copying the other object's state. If everything you need to set your state isn't inherent in the T, then you probably shouldn't be using operator=() for this purpose.
Consider splitting it into two separate functions, or move it into a non-operator function altogether. This way, it will be more clear to other people reading your code in the future what it is you're doing anyway.

Disclaimer, I really don't understand what you want to achieve, and I absolutely agree wit hte others, that you're probably trying doing something with the assignment operator that it's not intender for.
But here's a piece of code that is at least syntactically close to your requirement. Instead of specifying the two parameters in the operator declaration, you could make Test a class template and use plain old function overloading.
template<typename T1, typename T2>
struct Test {
T2 operator=(T1&) { return {}; }
T1 operator=(T2&) { return {}; }
};
struct Foo {};
int main() {
Test<int, Foo> t;
int gg = 1234;
Foo x = t = gg; // <- this really looks weird, but works.
Foo ff;
int i = t = ff;
}
If this is not what you want, then we proably need more information about your comcreate use case.
Live code here.

It sounds like what you actually want to do is derive a second type from your first template argument. This would normally be achieved through some sort of traits type:
#include <cstdio>
template < typename T >
struct TestTraits;
template <>
struct TestTraits< int >
{
typedef bool T2;
};
class Test
{
public:
template<typename T>
bool operator =(T& value)
{
using T2 = typename TestTraits< T >::T2;
return true;
}
};
int main()
{
Test t;
int gg = 1234;
t = gg;
}

Related

Overload operator [] with a template [duplicate]

In C++, can you have a templated operator on a class? Like so:
class MyClass {
public:
template<class T>
T operator()() { /* return some T */ };
}
This actually seems to compile just fine, but the confusion comes in how one would use it:
MyClass c;
int i = c<int>(); // This doesn't work
int i = (int)c(); // Neither does this*
The fact that it compiles at all suggests to me that it's doable, I'm just at a loss for how to use it! Any suggestions, or is this method of use a non-starter?
You need to specify T.
int i = c.operator()<int>();
Unfortunately, you can't use the function call syntax directly in this case.
Edit: Oh, and you're missing public: at the beginning of the class definition.
You're basically right. It is legal to define templated operators, but they can't be called directly with explicit template arguments.
If you have this operator:
template <typename T>
T operator()();
as in your example, it can only be called like this:
int i = c.operator()<int>();
Of course, if the template argument could be deduced from the arguments, you could still call it the normal way:
template <typename T>
T operator()(T value);
c(42); // would call operator()<int>
An alternative could be to make the argument a reference, and store the output there, instead of returning it:
template <typename T>
void operator()(T& value);
So instead of this:
int r = c.operator()<int>();
you could do
int r;
c(r);
Or perhaps you should just define a simple get<T>() function instead of using the operator.
Aren't you thinking of
class Foo {
public:
template<typename T>
operator T() const { return T(42); }
};
Foo foo;
int i = (int) foo; // less evil: static_cast<int>(foo);
live example. This proves you do not need to specify the template argument, despite the claim in the accepted answer.

Template parameter can't be deduced on implicitly constructed argument

I would like to have the following code in c++17:
#include <iostream>
#include <string>
#include <type_traits>
#include <functional>
class Foo;
template<class T>
class Bar {
public:
std::function<T(Foo&)> m_fn;
template<class Fn>
Bar(Fn fn) : m_fn(fn) {};
T thing(Foo &foo) const {
return m_fn(foo);
}
};
template<class Fn>
Bar(Fn) -> Bar<decltype(std::invoke(std::declval<Fn>(),
std::declval<Foo&>()))>;
class Foo {
public:
Foo() {};
template<class T>
std::vector<T> do_thing(const Bar<T> &b) {
std::vector<T> r;
r.push_back(b.thing(*this));
return r;
}
};
std::string test(Foo &) {
return "hello";
}
int main() {
Foo foo = Foo();
// works
std::vector<std::string> s = foo.do_thing(Bar{test});
// cant deduce T parameter to do_thing
std::vector<std::string> s = foo.do_thing({test});
}
But compiling this gives me "couldn't deduce template parameter ‘T’" on the call to do_thing.
Having do_thing(Bar{test}) fixes this and works fine but equates to some ugly code in the real code equivalent. I would like to have do_thing({test}) or do_thing(test) implicitly construct a Bar and pass that as the argument if possible.
I also don't want to forward declare a variable to pass into do_thing either
Is there some way to guide the inference of template argument T so that the call to do_thing can stay clean?
Edit:
Sorry for the late edit, but the arguments to the Bar constructor are over simplified in the example I included. In reality, there is an extra parameter std::optional<std::string> desc = std::nullopt and that might change in the future (although unlikely). So constructing the Bar inside do_thing would be a bit hard to maintain...
would like to have do_thing({test}) or do_thing(test) implicitly construct a Bar and pass that as the argument if possible.
Unfortunately, when you call do_thing({test}) or do_thing(test), test (or {test}) isn't a Bar<T> object. So the compiler can't deduce the T type and can't construct a Bar<T> object.
A sort of chicken-and-egg problem.
The best I can imagine is to add, in Foo, a do_test() method as follows
template<typename T>
auto do_thing (T const & t)
{ return do_thing(Bar{t}); }
This way you can call (without graphs)
std::vector<std::string> s = foo.do_thing(test);
You get the same result as
std::vector<std::string> s = foo.do_thing(Bar{test});
-- EDIT --
The OP ask
is there any way of preserving the {test} brace syntax? maybe with initializer_list or something?
Yes... with std::initializer_list
template<typename T>
auto do_thing (std::initializer_list<T> const & l)
{ return do_thing(Bar{*(l.begin())}); }
but, this way, you accept also
std::vector<std::string> s = foo.do_thing(Bar{test1, test2, test3});
using only test1
Maybe a little better... another way can be through a C-style array
template <typename T>
auto do_thing (T const (&arr)[1])
{ return do_thing(arr[0]); }
This way you accept only an element.
This happens because {} is not an expression and can only be used in limited ways while doing argument deduction, the parameter must have specific forms in order to succeed.
The allowed parameters types that can be used to deduce template parameters when {} is involved are better expanded in [temp.deduct.call]/1, two of the examples extracted from the cited part of the standard are:
template<class T> void f(std::initializer_list<T>);
f({1,2,3}); // T deduced to int
template<class T, int N> void h(T const(&)[N]);
h({1,2,3}); // T deduced to int
In your example the deduction guide is not used to deduce the T for {test} for the same as above.
foo.do_thing(Bar{test});
is your direct option without using additional functions.

C++ Template: typename and function to map to int

I'm writing a C++ template that needs two params: typename T, and an arbitrary function that maps T to an unsigned int.
How can I declare and use a template that can do that? I'd like to keep it simple, so that any dumb function can be used.
UPDATE:
Here is an example of what I'd like to do:
template<typename T, function f> // f has signature: unsigned int f(T);
class SortedContainer {
...
}
And, in this file:
unsigned int weight(Package p) { return p.w; }
SortedContainer<Package, &weight> sc;
UPDATE upon writing code
Based on the answers, I tried writing code, but it won't compile. Or rather, the template will compile, but not the test which invokes it.
The template code looks like this:
template<typename T, typename f>
class C {
...f(T)...
...
The invocation code looks like:
struct S {
int operator()(const int n) {
return n; // Dummy test code
}
};
...C<int, S>&...
The error message is:
error: no matching function for call to 'S::S(const int&)'
note: candidates are:
note: S::S()
It seems like it's trying to use S's constructor for some reason, as opposed to using the operator() which I want it to do.
The purpose of the f parameter is that the SortedContainer needs to be able to position T by an integer value. T is not necessarily an integer or even Comparable, so the caller, when instantiating a SortedContainer, needs to pass not only type T, but a function f to transform T to an integer.
The common way of doing this is to accept a general type F for the function. This will allow any kind of function-like object, whether it is a function pointer or a class object with an overloaded operator(). So:
template<class T, class F>
class SortedContainer {
// ...
}
Compare with things like std::map which does exactly this.
The disadvantage of this is that you cannot control what the prototype of the function is. This may or may not be a problem. One way is just to use it as if it was T-to-unsigned int and rely on the fact that the type system will catch any errors at the point of use.
Another way would be to verify the constraint with some kind of type trait. An example:
static_assert(std::is_same<unsigned int,
typename std::result_of<F(T)>::type>::value,
"Function must be T-to-unsigned int");
Edit: I wrote a small example to convince myself i got the assert right, might as well post it. Here, using A will compile OK but B will fail the assertion.
#include <type_traits>
template<class T, class F>
class SortedContainer {
static_assert(std::is_same<unsigned int,
typename std::result_of<F(T)>::type>::value,
"Function must be T-to-unsigned int");
};
struct A {
unsigned int operator()(double) { return 0; }
};
struct B {
double operator()(double) { return 0; }
};
int main() {
SortedContainer<double, A> a;
SortedContainer<double, B> b;
}
Based on your other edit:
Note that the templated type F only captures the type of the function. You still need an object of this type - the actual function - to call. Again, compare with std::map which first is templated to take a comparator type, and then has a constructor that takes an object of this type. This is true even if you use a normal function - the type will be SortedContainer<T, unsigned int (*)(T)>, but you would somehow need to pass the actual function pointer into the container (probably through the constructor).
Something like this:
template<class T, class F>
class SortedContainer {
public:
SortedContainer(F f = F()): func(f) {}
void foo() {
// ...
func();
// ...
}
private:
F func;
};
struct A {
unsigned int operator()() { return 0; }
};
int main() {
A a;
SortedContainer<double, A> c(a);
c.foo();
}
IMO, you don't require a separate template argument for Function F.
template<typename T> // F not required!
class SortedContainer {
...
};
Choose a good name and use that function by overloading it for various cases. e.g. to_uint()
Since you want to map (i.e. relate) a type to an unsigned int (uint), use following function in global scope:
template<typename T>
uint to_uint (const T& t) {
return t.to_uint(); // Must have `uint to_uint() const` member, else error
}
// Overloads of `to_uint()` for PODs (if needed)
template<typename T> // For all kinds of pointers
uint to_uint (const T* const pT) {
if(pT == nullptr)
<error handling>;
return to_uint(*pT);
}
Scenario: For Sorted_Container<X>, whenever to_uint(x) is invoked, then:
If X is a class, then it must have uint to_uint() const method
Else if X is a POD, then you may have to overload to_uint() for that type
Else, the compiler will generate an error
It's as you said, pretty much:
template< typename T, unsigned int f(T) >
struct SortedContainer;
...
SortedContainer<Package, weight> sc;
if you actually wanted the argument to be a function pointer rather than a function,
template< typename T, unsigned int (*f)(T) >
and similarly if you want the argument to be a function reference.
(naturally, this will only work for dumb functions, not for function objects with an operator() operator of the right signature)
You may use C-style function pointers as #Hurkyl suggests, or std::function which probably can't be template parameters, but I think that idea is wrong.
C++ templates are duck-typed, so STL code in many places (std::unordered_map -> std::hash, std::sort -> std::less) relies on that. I think you should also apply this approach - just ask user to provide specialization for type T:
/* Universal implementation */
template<typename T>
unsigned int sorted_container_weight(T t) { return t; }
template<typename T>
class SortedContainer {
T t;
public:
unsigned int somefunc() {
return sorted_container_weight(t);
}
};
template<>
unsigned int sorted_container_weight<Package>(Package p) { return p.w; }
SortedContainer<Package> sc;

Making is_reference work inside a function template for reference without knowing the type on which the function is to be instantiated

Here is the code
#include <iostream>
template<typename T>
class IsReference {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(C*);
template<typename C> static Two test(...);
public:
enum { val = sizeof(IsReference<T>::test<T>(0)) == 1 };
enum {result = !val};
};
template < class T>
void foo(T t){
std::cout<< IsReference<T>::result << "\n";
}
class C{
public: int a;
};
int main(){
C c1;
C &c2 = c1;
foo(c1);
foo(c2);
}
The output is 0 for foo(c2) also which I don't want.
Inside foo how can I check whether T is reference type or not?
I dont want to call foo as foo<C&>(c2) i.e we don't know on which type the function is getting instantiated.
You can't. As an expression, c2 is an lvalue of type C, just like c1, so in both cases argument-dependent lookup will choose foo<C>.
You get the same result using Boost.TypeTraits.
There may be some new wizardry in C++11 to help, but (a) I don't know enough about C++11 to answer, and (b) you've tagged the question C++03 anyway.
For edited question:
I dont want to call foo as foo<C&>(c2) i.e we don't know on which type
the function is getting instantiated
It's not possible. Because, when an object or reference are passed to a function, the function is not aware whether it's an object or reference.
Here is one proposed C++11 solution (using macro);
#define FOO(X) foo<decltype(X)>(X)
Use FOO instead of foo
I think it should be
template<typename C> static One test(C&);
rather than One test(C*)

Function template with an operator

In C++, can you have a templated operator on a class? Like so:
class MyClass {
public:
template<class T>
T operator()() { /* return some T */ };
}
This actually seems to compile just fine, but the confusion comes in how one would use it:
MyClass c;
int i = c<int>(); // This doesn't work
int i = (int)c(); // Neither does this*
The fact that it compiles at all suggests to me that it's doable, I'm just at a loss for how to use it! Any suggestions, or is this method of use a non-starter?
You need to specify T.
int i = c.operator()<int>();
Unfortunately, you can't use the function call syntax directly in this case.
Edit: Oh, and you're missing public: at the beginning of the class definition.
You're basically right. It is legal to define templated operators, but they can't be called directly with explicit template arguments.
If you have this operator:
template <typename T>
T operator()();
as in your example, it can only be called like this:
int i = c.operator()<int>();
Of course, if the template argument could be deduced from the arguments, you could still call it the normal way:
template <typename T>
T operator()(T value);
c(42); // would call operator()<int>
An alternative could be to make the argument a reference, and store the output there, instead of returning it:
template <typename T>
void operator()(T& value);
So instead of this:
int r = c.operator()<int>();
you could do
int r;
c(r);
Or perhaps you should just define a simple get<T>() function instead of using the operator.
Aren't you thinking of
class Foo {
public:
template<typename T>
operator T() const { return T(42); }
};
Foo foo;
int i = (int) foo; // less evil: static_cast<int>(foo);
live example. This proves you do not need to specify the template argument, despite the claim in the accepted answer.