Type checking template class parameters - c++

I am trying to achieve type checking of template class parameters by disallowing implicit type conversions such as string->bool thereby throwing compile error.
The specific scenario is a simple one as follows:
#include <iostream>
#include <string>
using namespace std;
template <class T>
class myPair {
T a, b;
public:
myPair(T first, T second ) {
a = first;
b = second;
}
void test();
};
typedef myPair<bool> boolParm;
template<class T>
void myPair<T>::test() {
if(a == true) {
cout << "a is true" << endl;
} else {
cout << "a is false" << endl;
}
if(b == true) {
cout << "b is true" << endl;
} else {
cout << "b is false" << endl;
}
}
int main() {
boolParm myObj(false, "false");
myObj.test();
return 0;
}
The output of the above scenario is undesirable since the user may inadvertently pass 2 different types: bool and string and receive the first one as false (correct since passed as bool) but the second one will be true (incorrect since implicit type conversion from string to bool).
I wish to restrict the user code in main() to throw compile errors and disallowing string/int parameters to pass in the constructor. It should only allow bool.
I tried by using an overloaded constructor myPair(bool first, string second) but it didn't match since I guess the implicit type conversion from string->bool happens before the constructor is called.
Is there any solution using template specializations in this scenario?
Any help is highly appreciated
Thanks

One workaround is to add a templated factory function to create the myPair.
template <typename T>
myPair<T> makeParam(T a, T b) {
return myPair<T>(a, b);
}
That will fail to compile with ambiguous template parameter T if the types don't match. You can extend this with template specializations explicitly forbidding certain types for T. Your main function will then look something like:
int main() {
boolParm myObj = makeParam(false, "false");
myObj.test();
return 0;
}
Alternatively change the constructor:
template <typename U, typename V>
myPair(U a, V b);
And specialize as necessary
An example of such specialization:
template <class T>
class myPair {
T a, b;
public:
template <typename U, typename V> // generic version
myPair(U first, V second)
{
// intentionally fail to compile
static_assert(false, "don't support generic types");
}
template <> // template specialization
myPair(T first, T second)
{
// explicitly require exactly type T
a = first;
b = second;
}
};

It is indeed weird behavior at first glance; but as far as I can say, you can't prohibit that - not for primitive types like bool, anyway.
The implicit conversion of parameters happen before you get a say on it, and it seems there is an implicit primitive type conversion from char const * to bool.
See also e.g. this other question: Why does a quoted string match bool method signature before a std::string?

Related

Type conversion in class template instantiation

I have a template class item which stores objects of various types T. It also attaches attributes to those objects in instantiation/initialization.
One special thing I want to achieve is that whenever item sees a const char *, it deems and stores it as a std::string. This could be done, as follows.
But in type checking, I found an item instantiated from a const char * is still different in type from an item instantiated from a std::string. Please see the last line with comment false, which I want to make true.
#include <iostream>
#include <string>
#include <type_traits>
using namespace std;
template<typename T>
using bar = typename std::conditional<std::is_same<T, const char *>::value,
string, T>::type;
template<typename T>
class item
{
bar<T> thing;
// other attributes ...
public:
item(T t) : thing(t) {}
// other constructors ...
bar<T> what() const
{
return thing;
}
};
int main()
{
auto a = item("const char *"); // class template argument deduction (C++17)
auto b = item(string("string")); // class template argument deduction (C++17)
cout << std::boolalpha;
cout << (typeid(a.what()) == typeid(b.what())) << endl; // true
cout << (typeid(a) == typeid(b)) << endl; // false
}
My question is: is it possible to make any change to the template class item so that an item instantiated from a const char * becomes the same in type with an item instantiated from a std::string?
In other words, can I make any change to the design of the template class item so that typeid(a) == typeid(b) evaluates to true ?
Thank you !
Note: This follows up a previous question on template function. But I think there's something intrinsically different that it deserves a stand-alone question.
Edit: My goal is to change the design of the template class item (e.g. item signatures), not the code in main, which is assumed to be supplied by users. I want to make life easier for the users of item, by not asking them to explicitly supply type T in instantiation. This is meant to be done by C++17 template class argument deduction or some equivalent workarounds.
Update: Thank you all! Special thanks to #xskxzr, whose one-liner exactly solves my question. With user-defined deduction guides for class template argument deduction, I don't even need the bar<T> technique in my previous code. I put updated code below for your comparison.
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class item
{
// UPDATE: no bar<T> needed any more
T thing;
// other attributes ...
public:
item(T t) : thing(t) {}
// other constructors ...
// UPDATE: no bar<T> needed any more
T what() const
{
return thing;
}
};
item(const char *) -> item<std::string>; // UPDATE: user-defined deduction guide !
int main()
{
auto a = item("const char *"); // class template argument deduction (C++17)
auto b = item(string("string")); // class template argument deduction (C++17)
cout << std::boolalpha;
cout << (typeid(a.what()) == typeid(b.what())) << endl; // true
cout << (typeid(a) == typeid(b)) << endl; // UPDATE: now true !
}
You can add a user-defined deduction guide:
item(const char *) -> item<std::string>;
With this deduction guide, a will be deduced to be item<std::string>.
No, you can't directly make the typeid of two templated objects using different template arguements be the same.
But to achieve your end goal you can use a factory like pattern. It could look something like this:
template<typename T, typename R = T>
item<R> make_item(T&& t)
{
return item<T>(std::forward<T>(t));
}
// Specialization for const char *
template<>
item<std::string> make_item(const char *&& str)
{
return item<std::string>(str);
}
The downside with this approach is that you'll need to construct all of your objects with this factory. And if you have a lot of exceptions you'll need to make a specialization for each exception.
This is more a guess than an answer, but I'd say no. Templates are expanded at compile time, so because you are creating an
item<const char*>
and an
item<std::string>
then the code that gets expanded looks something like
class item1
{
bar<const char*> thing;
// other attributes ...
public:
item(const char* t) : thing(t) {}
// other constructors ...
bar<const char*> what() const
{
return thing;
}
};
class item2
{
bar<std::string> thing;
// other attributes ...
public:
item(std::string t) : thing(t) {}
// other constructors ...
bar<std::string> what() const
{
return thing;
}
};
(More or less; they wouldn't actually be called item1 and item2)
How you chose to evaluate these two types is up to you, but to the compiler they are in fact two different types.
Ok, I'd never seen or used std::conditional before so I wasn't sure what that was doing, but after reading up on it and playing around with your code I did get it to "work" by using
bar<T>
as the template type. So instead of
auto a = item<const char*>("const char *");
auto b = item<string>(string("string"));
I did
auto a = item<bar<const char*>>("const char *");
auto b = item<bar<string>>(string("string"));
The thing is you need the template type to be the same in both cases, meaning the type needs to resolve to std::string before the template gets expanded. As long as you use your conditional, you can define any type.
auto c = item<bar<int>>(5);
Not sure that's a good solution (which is why I said "work"), but see my other answer about the class types actually being different.

C++ templates: Specialized member function to resolve case of ambiguous overload in primary template

I have a template where a function is overloaded so it can handle both an std::string parameter and the type of parameter that the template gets instantiated with. This works fine except when the template is being instantiated with std::string, since this results in two member functions with the same prototype. Thus, I have chosen to specialize that function for this particular case. However, it seems like the compiler (g++ 4.8.1 with flag -std=c++0x) never gets to the point where the specialization is actually overriding the primary template and it complains about the ambiguous overload the before it seems to realize that it should use the specialization. Is there a way to get around this?
#include <iostream>
template<class T>
struct A {
std::string foo(std::string s) { return "ptemplate: foo_string"; }
std::string foo(T e) { return "ptemplate: foo_T"; }
};
template<> //Error!
std::string A<std::string>::foo(std::string s) { return "stemplate: foo_string"; }
int main() {
A<int> a; //Ok!
std::cout << a.foo(10) << std::endl;
std::cout << a.foo("10") << std::endl;
//A<std::string> b; //Error!
//std::cout << a.foo("10") << std::endl;
return 0;
}
This results in compile errors, even if I don't instantiate at all with std::string (it seems that the compiler instantiates with std::string as soon as it sees the specialization and that it, before it actually processes the specialization, complains about the ambiguous overload which the specialization, in turn, will "disambiguate").
Compiler output:
p.cpp: In instantiation of 'struct A<std::basic_string<char> >':
p.cpp:10:27: required from here
p.cpp:6:14: error: 'std::string A<T>::foo(T) [with T = std::basic_string<char>; std::string = std::basic_string<char>]' cannot be overloaded
std::string foo(T e) { return "ptemplate: foo_T"; }
^
p.cpp:5:14: error: with 'std::string A<T>::foo(std::string) [with T = std::basic_string<char>; std::string = std::basic_string<char>]'
std::string foo(std::string s) { return "ptemplate: foo_string"; }
^
I would like it to just skip through the implementation of foo() in the primary template and use the specialization without considering the primary template foo(). Could it be done somehow, maybe with non-type template parameters, or do I have to make a fully specialized class template for std::string with all the code duplication it implies (I prefer not to use inheritance here)... Other suggestions?
When you specilize your member function you still get the double ambiguous declaration. Waht you need is to specialize the struct template:
template<>
struct A<std::string> {
std::string foo(std::string s) { return "ptemplate: foo_string"; }
};
If there are many members to the A struct maybe you can refactor:
template<typename T>
struct Afoo
{
std::string foo(T s) { ... }
std::string foo(std::string s) { ... }
};
template<>
struct Afoo<std::string>
{
std::string foo(std::string s) { ... }
};
template<typename T>
struct A : Afoo<T>
{
//a lot of code
};
I'm going to answer this myself since I've been diving deep into this subject today and I think these solutions are nice. All other posts up to this point have been contributive and have had attractive details with potential in other situations. However, I preferred to do it with these things in mind:
Avoid the use of more than one class template
Avoid too complicated specializations as far as possible
Avoid using inheritance and refactor into base and derived classes
Avoid the use of extra wrappers
Please feel free to comment before I accept it as my answer.
Another good and inspiring post on the subject focusing on the use of member function overloading rather than specializations can be found at explicit specialization of template class member function
Solution 1
template<class T>
struct A {
template<class V = T> std::string foo(T) { return "foo_T"; }
std::string foo(std::string) { return "foo_std::string"; }
std::string foo(const char *) { return "foo_const char *"; }
};
template<> template<>
std::string A<std::string>::foo(std::string s) { return foo(s); }
I think this is a dense and understandable solution allowing all class instantiations to use foo(std::string) and foo(const char *) (for passing a string as an rvalue). The use of a dummy template parameter effectively stops class instantiations with std::string from resulting in ambiguous overloads at the same time as the actual template argument hinders uncontrolled function instantiations with unpredictable function arguments. The only problem might come from a class instantiation with std::string that might use the template instead of the regular member function if explicitly called with foo<std::string>(std::string) in which way I would want the class to use the regular foo(std::string) instead of the function template for other instantiations. This is resolved by using a single template specialization.
Solution 2
template<class T>
struct A {
template<class V> std::string foo(V s) { return foo_private(s); }
private:
template<class V = T> std::string foo_private(T) { return "foo_T"; }
std::string foo_private(const char *) { return "foo_const char *"; }
std::string foo_private(std::string) { return "foo_std::string"; }
};
This version allows us to skip the specialization to the benefit of a second template in the class declaration.
Both versions used with:
int main() {
A<int> a;
std::cout << a.foo(10) << std::endl;
std::cout << a.foo("10") << std::endl;
A<std::string> b;
std::cout << b.foo<std::string>("10") << std::endl;
std::cout << b.foo("10") << std::endl;
return 0;
}
... outputted:
foo_T
foo_const char *
foo_const char *
foo_std::string
The error is saying that you ended up creating two method with the same signature.
That is because the struct has been templated with a std::string as parameter.
You should made the function as a templated function, using its own template parameters 'K' not related to the structure template parameter 'T'. Then you can achieve template specialization for the function only.
I admit that the solution I offer below, is a hacky solution indeed, but it does accomplish what you're trying to do and it's kinda funny. Please consider it thoroughly before you use this ;-)
I work around the issue by creating a new type, called FakeType, which can be constructed from your template-type T. The second overload of foo is now for FakeType<T> instead of T, so even when T == string there will be two different overloads:
template <typename T>
struct FakeType
{
T t;
FakeType(T const &t_): t(t_) {}
operator T() { return t; }
};
template <typename T>
struct A
{
string foo(string s) { return "ptemplate: foo_string"; }
string foo(FakeType<T> e) { return "ptemplate: foo_T"; }
};
For the case that T != string:
A<int>().foo("string"); // will call foo(string s)
A<int>().foo(1); // will call foo(FakeType<int> e)
In the latter case, the int will be promoted to a FakeType<int>, which can be used as a regular int through the conversion operator.
For the case that T == string:
A<string>().foo("string"); // will still call foo(string s)
Because the compiler will always prefer an overload for which no promotion is necessary.
PS. This approach assumes that foo is going to get its arguments either by value, or by const-reference. It will break as soon as you try to pass by reference (this can be fixed).

non-type template parameter of user-defined type

I'm trying to define a template-class that has a non-type template parameter of a user-defined type. Unfortunately, without success so far. The real code is a bit too lengthy, but a simplified example looks like this:
#include <iostream>
template <class T>
class Maybe {
bool is_ = false;
T value_;
public:
constexpr Maybe() = default;
constexpr Maybe(T value) : is_(true), value_(value) {}
constexpr bool is() const { return is_; }
};
template <Maybe<int> parm>
struct Test {
void say() const {
std::cout << "parm is " << (parm.is() ? "set" : "not set") << ".\n";
}
};
int main() {
Test<Maybe<int>{}> not_set;
Test<Maybe<int>(2)> is_set;
not_set.say();
is_set.say();
}
When I try to compile this code (with Clang 3.4) I get the following error message:
test.cc:15:22: error: a non-type template parameter cannot have type
'Maybe<int>'
template <Maybe<int> parm>
^
test.cc:23:10: error: value of type 'Maybe<int>' is not implicitly
convertible to 'int'
Test<Maybe<int>{}> not_set;
^~~~~~~~~~~~
test.cc:24:10: error: value of type 'Maybe<int>' is not implicitly
convertible to 'int'
Test<Maybe<int>(2)> is_set;
^~~~~~~~~~~~~
3 errors generated.
Now, I know that a non-type template parameter has to fulfill some conditions. However, I thought that being constexpr would be sufficient. Or can it really only be one of the built-in integral types?
Is there a way to pass non-type template parameters of my own user-defined type?
No, you cant.
n3376 14.1/7
A non-type template-parameter shall not be declared to have floating
point, class, or void type.
template<double d> class X; // error
template<double* pd> class Y; // OK
template<double& rd> class Z; // OK
so, you can pass pointer, or reference, but not object of class-type.
live example
this whole problem is the very reason why rational numbers are implemented as template-class with the actual numbers in the template parameters. to get a template-parameter storing more than just an int, you must create a similar class as std::ratio which also is evaluated at compile-time only.
as for your actual example, consider writing something similar to:
template<class T, T ... Params>
class Maybe {
and then
Test<Maybe<int,5> > is_set;
or
Test<Maybe<int> > not_set;
I know that this question is old, but I would like to point out a template meta programming approach to this.
In c++ you can pass any type to a template like that, so why not just make it a type?
In order to do that, you will want to make a wrapper class for your Maybe class:
template <typename T>
struct maybe_wrap
{
Maybe<T> value{};
}
template<typename T, T value>
struct maybe_wrap
{
Maybe<T> value{value};
}
Then just pass the maybe_wrap as a typename and just access the maybe_wrap<int, 3>().value of it when you need it!
The only restriction of this is that T might not be one of the non-type values (int, bool, etc).
In that case, just use the above logic again!
In C++20, this is possible using structural literal class types:
#include <iostream>
struct NullOptT {} NullOpt;
/**
* Literal class type.
*
* Represents an optionally provided `int`.
*/
struct OptionalInt {
constexpr OptionalInt(NullOptT) {}
constexpr OptionalInt(int value): has_value(true), value(value) {}
const bool has_value = false;
const uint32_t value {};
};
/**
* Prints whether or not a value was provided for "maybe" WITHOUT branching :)
*/
template<OptionalInt maybe>
void Print() {
if constexpr(maybe.has_value) {
std::cout << "Value is: " << maybe.value << std::endl;
} else {
std::cout << "No value." << std::endl;
}
}
// Note: implicit conversions are at play!
int main()
{
Print<123>(); // Prints "Value is: 123"
Print<NullOpt>(); // Prints "No value."
}
I wrote up a blog post with more detail on the usage and restrictions of class literal NTTPs here: Literal Classes as Non-type Template Parameters in C++20.

What's the use of second parameter of std::enable_if?

I am confused about the second parameter of std::enable_if.
In using of a return type of int, we can make it using:
template <class T>
typename std::enable_if<mpi::is_builtin<T>::value, int>::type
foo() { return 1; }
But how can I use enable_if in paramter or template? In this case, what's the difference of too functions below:
template<class T ,
class = typename std::enable_if<std::is_integral<T>::value>::type >
T too(T t) { std::cout << "here" << std::endl; return t; }
int too(int t) { std::cout << "there" << std::endl; return t; }
Thanks.
It means that in case of
template<class T ,
class = typename std::enable_if<std::is_integral<T>::value>::type >
it becomes
template<class T ,
class = void >
if the condition std::is_integral<T>::value is true, hence the function is allowed for the type T and therefore participates in overload resolution.
If the condition is not met, it becomes illegal and the typename std::enable_if<...>::type invalidates the function for the type T. In your example, the first method allows all integral types (int, unsigned, long, ...) but no classes, etc.
The second, int-only version in your example would loose some information and convert values from unsigned to signed or narrow some values, which is why the first version can be really helpful in some cases.
Note that void is actually the default for the second parameter of std::enable_if, which is often sufficient to enable or disable templates, etc. as you don't really need a specific type. All you need to know/detect is, whether or not it is valid (void) or invalid, in which case there is no valid substitution for the ::type part.
what's the difference of too functions below:
One is a template that can be called for any CopyConstructible type, the enable_if only constrains it when the default template argument is used:
#include <iostream>
template<class T ,
class = typename std::enable_if<std::is_integral<T>::value>::type >
T too(T t) { std::cout << "here" << std::endl; return t; }
int too(int t) { std::cout << "there" << std::endl; return t; }
int main()
{
too<double, void>(1.0);
}

Template argument deduction (using both explicit and implicit arguments in same call)

I have three template arguments to a function and am having troubles with (I think) the compiler deducing which template argument is which.
The template function is:
#include <structures/partition.h>
#include <vector>
namespace cliques
{
template <typename P, typename T, typename QF>
P find_optimal_partition_louvain(cliques::Graph<T> &my_graph,
QF quality_function)
{
P dummy;
}
}
And when I try to call it with
cliques::find_optimal_partition_louvain<cliques::DisjointSetForest>(my_new_graph, cliques::find_linearised_stability(current_markov_time));
Where template argument P should correspond to cliques::DisjointSetForest, and the normal function arguments are a templated class and a function object.
This fails with
error: no matching function for call to
find_optimal_partition_louvain(cliques::Graph<lemon::ListGraph>&,
cliques::find_linearised_stability)
However if I use a builtin type such as an int or a float for the P parameter everything compiles fine.
e.g.
cliques::find_optimal_partition_louvain<int>(my_new_graph, cliques::find_linearised_stability(current_markov_time));
So my question is what am I doing wrong here, how can I use a better inform the compiler which parameter is which, or am I completely off track?
I hate to answer my own question but problem was that cliques::DisjointSubsetForest is actually a templated class so
cliques::find_optimal_partition_louvain<cliques::DisjointSetForest<int> >(my_new_graph, cliques::find_linearised_stability(current_markov_time));
works
"error: no matching function for call to ‘find_optimal_partition_louvain(cliques::Graph&, cliques::find_linearised_stability)"
It would seem that your compiler thinks that cliques::Graph is not a template.
I have tried to reproduce the error on a simple example, but I failed to do so (on gcc).
It looks as though the compiler does figure out that find_optimal_partition_louvain is a function template. I suggest trying the following:
cliques::template find_optimal_partition_louvain<cliques::DisjointSetForest>(my_new_graph, cliques::find_linearised_stability(current_markov_time));
Otherwise, you might want to verify if the following simple example compiles fine on your compiler (because it should!):
#include <iostream>
template <class G>
struct Bar { };
namespace Foo {
template <class A, class B, class C>
A some_function(Bar<B>&, C aFunc) {
aFunc();
return A();
};
};
struct HWPrinter {
HWPrinter() { std::cout << "Hello World!" << std::endl; };
};
struct IntPrinter {
int value;
IntPrinter(int aValue) : value(aValue) { };
void operator() () { std::cout << "The integer is: " << value << std::endl; };
};
int main() {
Bar<HWPrinter> ab;
Foo::some_function<HWPrinter>(ab,IntPrinter(42));
return 0;
};