I've this code:
class XMLNode
{
//...
template <typename T>
bool getValue(T& t, const std::string& path) const
{
if (empty())
{
throw std::runtime_error("Empty node");
}
return nsXML::getValue(t, path, *node);
}
template <typename T>
T getValue(const std::string& path) const
{
if (empty())
{
throw std::runtime_error("Empty node");
}
return nsXML::getValue<T>(path, *node);
}
//...
};
class XMLData
{
//...
template <typename T>
T getValue(const std::string& path)
{
return XMLNode(&mDocNode, 0).getValue(path); // ERROR LINE
}
//...
};
And gives me error
no matching function for call to ‘nsXML::XMLNode::getValue(const string&)’
note: candidates are:
note: template<class T> bool nsXML::XMLNode::getValue(T&, const string&) const
note: template<class T> T nsXML::XMLNode::getValue(const string&) const
Why does g++ give me this error?
The compiler has no way to assess with which type you want to instantiate the function template. In this case you have to specify it explicitly:
return XMLNode(&mDocNode, 0).getValue<T>(path);
// ^-- explicit instantiation
Only in some cases the template argument can be deduced by the compiler automatically from the function arguments:
int i;
bool b = XMLNode(&mDocNode, 0).getValue(i, path);
Here, the compiler sees an int as the first function argument and can deduce T for this function call to be int, so its the same as
bool b = XMLNode(&mDocNode, 0).getValue<int>(i, path);
because your XMLNode::getValue(const std::string& path) function is a const, so when it's calling nsXML::getValue, it's looking for the const version, I guess there isn't a const one defined.
Note a const member function and a non const member function are different.
Related
I've created the following class:
using namespace std;
typedef std::variant<long, bool> value;
class settings {
public:
template<class T>
void set(const string &name, const T &val) noexcept {
data_[name] = val;
}
template<class T>
[[nodiscard]] inline const T &get_or_default(const string &name, const T &val) noexcept {
if (data_.contains(name)) {
if (auto value = get_if<T>(&data_[name]); value) {
return *value;
}
}
return val;
}
private:
unordered_map<string, value> data_;
};
And this works fine, for example with this usage:
auto config = settings{};
config.set("foo", 100L);
config.set("bar", true);
cout << "foo: " << config.get_or_default("foo", 0L) << endl;
cout << "bar: " << config.get_or_default("bar", false) << endl;
cout << "foobar: " << config.get_or_default("foobar", 500L) << endl;
output
foo: 0
bar: 1
foobar: 500
If I try to use the method set with something that is not valid for the type value (bool or long):
auto config = settings{};
config.set("foo", 100.0);
We get an error like this:
\main.cpp(13): error C2679: binary '=': no operator found which takes a right-hand operand of type 'const T' (or there is no acceptable conversion)
with
[
T=double
]
variant(1200): note: could be 'std::variant<long,bool> &std::variant<long,bool>::operator =(std::variant<long,bool> &&)'
variant(1200): note: or 'std::variant<long,bool> &std::variant<long,bool>::operator =(const std::variant<long,bool> &)'
variant(1042): note: or 'std::variant<long,bool> &std::variant<long,bool>::operator =(_Ty &&) noexcept(<expr>)'
main.cpp(12): note: 'std::variant<long,bool> &std::variant<long,bool>::operator =(_Ty &&) noexcept(<expr>)': could not deduce template argument for '__formal'
main.cpp(13): note: while trying to match the argument list '(std::variant<long,bool>, const T)'
with
[
T=double
]
main.cpp(32): note: see reference to function template instantiation 'void settings::set<double>(const std::string &,const T &) noexcept' being compiled
with
[
T=double
]
Similar using the method get_or_default:
auto config = settings{};
cout << "foo: " << config.get_or_default("foo", 100) <<endl;
We get this error:
variant(1302): error C2338: static_assert failed: 'get_if<T>(variant<Types...> *) requires T to occur exactly once in Types. (N4835 [variant.get]/9)'
main.cpp(17): note: see reference to function template instantiation 'int *std::get_if<int,long,bool>(std::variant<long,bool> *) noexcept' being compiled
main.cpp(32): note: see reference to function template instantiation 'const T &settings::get_or_default<int>(const std::string &,const T &) noexcept' being compiled
with
[
T=int
]
These errors are fine, however I like to use c++ 20 concepts to restrict the types for the method set and get_or_default to give a more simple error to the user of the class that indicates that the types valid are only bool and long.
Is possible as well to give a specific assertion message for those messages using only concepts? Very specifically, not third parties libraries.
Add a declaration:
#include <type_traits>
template<typename T>
concept is_value=std::is_same_v<T,long> || std::is_same_v<T,bool>;
and then make a tiny change to the template:
template<is_value T>
[[nodiscard]] inline const T &get_or_default(const string &name, const T &val) noexcept {
That's it. Make the same change to the other template function.
Not using concept, but you could write your settings in a way that set/get_or_default are not template:
template <typename... Ts>
struct settings_base
{
std::unordered_map<std::string, std::variant<Ts...>> data_;
};
template <typename Base, typename T>
struct settings_node : virtual Base
{
void set(const std::string& name, const T& value)
{
Base::data_[name] = value;
}
[[nodiscard]] const T &get_or_default(const std::string &name, const T &val) noexcept
{
auto it = Base::data_.find(name);
if (it != Base::data_.end()) {
if (auto value = std::get_if<T>(&it->second); value) {
return *value;
}
}
return val;
}
};
template <typename... Ts>
class settings_impl : settings_node<settings_base<Ts...>, Ts>...
{
public:
using settings_node<settings_base<Ts...>, Ts>::set...;
using settings_node<settings_base<Ts...>, Ts>::get_or_default...;
// So equivalent to:
// void set(const std::string&, const T1& value);
// ..
// void set(const std::string&, const TN& value);
};
And then
using settings = settings_impl<bool, long /*std::string*/>;
That allows conversion contrary to template way.
Note though that "xxxx" (const char [N]) would select bool overload and not std::string one, due to overload rules.
Not a concept, but a different approach:
If you want to limit get and set to exactly long or bool, you can have non-template overloads:
class settings {
public:
void set(const std::string &name, const long &val) noexcept {
set_impl(name, val);
}
[[nodiscard]] const long &get_or_default(const std::string &name, const long &val) noexcept {
return get_impl(name, val);
}
void set(const std::string &name, const bool &val) noexcept {
set_impl(name, val);
}
[[nodiscard]] const bool &get_or_default(const std::string &name, const bool &val) noexcept {
return get_impl(name, val);
}
private:
template<class T>
void set_impl(const std::string &name, const T &val) noexcept {
data_[name] = val;
}
template<class T>
[[nodiscard]] const T &get_impl(const std::string &name, const T &val) noexcept {
if (data_.contains(name)) {
if (auto value = get_if<T>(&data_[name])) {
return *value;
}
}
return val;
}
std::unordered_map<std::string, value> data_;
};
So I was trying to write a default function version when there are no template arguments passed in the following code:
struct A{ int x; };
struct B : public A { int y;};
struct C {
void set(const A& v) { v_ = v; }
template <typename T>
const T& get() { return reinterpret_cast<T&>(const_cast<A&>(v_)); }
template <>
const A& get() { return v_;}
A v_;
};
int main() {
B b;
C c;
c.set(b);
(void)c.get<B>().y;
(void)c.get<A>().x;
//(void)c.get().x; << Error
}
But it doesn't work at the indicated Error line above with this error:
test.cpp:22:13: error: no matching member function for call to 'get'
(void)c.get().x;
~~^~~
test.cpp:9:14: note: candidate template ignored: couldn't infer template argument 'T'
const T& get() { return reinterpret_cast<T&>(const_cast<A&>(v_)); }
^
1 error generated.
I do understand the issue that since no template argument was passed in the client call, it was unable to infer the typename but is there a way to provide a default implementation like the one in the above code when that happens? Essentially I want to avoid writing client code as (void)c.get<A>().x; and instead, adopt the default route implicitly.
As an alternative solution, you can specify A as default template argument for parameter T, e.g.
struct C {
void set(const A& v) { v_ = v; }
template <typename T = A>
const T& get() { return reinterpret_cast<T&>(const_cast<A&>(v_)); }
template <>
const A& get() { return v_;}
A v_;
};
int main() {
B b;
C c;
c.set(b);
(void)c.get<B>().y; // call the primary template
(void)c.get<A>().x; // call the specification
(void)c.get().x; // call the specification
}
You can delete the template <> part.
struct C {
void set(const A& v) { v_ = v; }
template <typename T>
const T& get() { return reinterpret_cast<T&>(const_cast<A&>(v_)); }
const A& get() { return v_;}
A v_;
};
I am currently getting into templates and I am trying to fully specialize a class function here in my class (only relevant code):
class item_t
{
public:
template<class T>
void set(const std::string &key, const T &value);
template<>
void set(const std::string &key, const std::string &value);
};
which will result in this compiler error (gcc 6.3.0):
Fehler: explicit specialization in non-namespace scope ‘class base::data::item_t’
template<>
^
What am I missing here? From what I understood, it is not possible to partially specialize a function template, but this is a full specialization.
You cannot explicitly specialize a template member function inside the class definition. You have to do it outside (in a namespace scope):
class item_t
{
public:
template<class T>
void set(const std::string &key, const T &value);
};
template<>
void item_t::set<std::string>(const std::string &key, const std::string &value)
{
}
In your particular use case, you don't even need a specialization - an overload will work just fine:
template<class T>
void set(const std::string &key, const T &value);
void set(const std::string &key, const std::string &value);
I need to return the proper type as per the templatized argument. I am getting error as below:
Can someone please suggest whats the solution for this? Thanks in advance.
error: no matching function for call to âsecond::second(const std::basic_string<char,
std::char_traits<char>, std::allocator<char> >&)â
note: candidates are: second::second(const std::string&, const std::string&)
note: second::second(const second&)
Code is as below:
struct first
{
public:
const string &str;
first(const string & str) : str(str) { }
};
struct second : public first
{
public:
const string &str2;
second(const string &str1, const string &str2) : first(str1), str2(str2)
{ }
};
class base
{
public:
template<class T>
inline T fun(const string &s1, const string &s2);// { cout<<" T = "<<a; }
};
template<class T>
inline T base::fun(const string &s1, const string &s2)
{
if(1)
return T(s1);
else
return T(s1, s2);
}
int main()
{
string a = "a";
string bb = "b";
base b;
b.fun<first>(a, bb);
b.fun<second>(a, bb);
return 0;
}
The problem is that you can't create a function template which always accepts two arguments of fixed types, and returns objects of different types depending on template parameter. The reason is that you can't specialize template functions, you can only overload them, and you can't make overloaded functions differ only by return type.
What you can do is use SFINAE. This way at most one function will be present for given template parameter:
class base {
public:
template<typename T, typename = typename std::enable_if<std::is_same<T, first>::value>::type>
first fun(const string &s1, const string &s2) {
return first(s1);
}
template<typename T, typename = typename std::enable_if<std::is_same<T, second>::value>::type>
second fun(const string &s1, const string &s2) {
return second(s1, s2);
}
};
Alternatively you can make base templated and specialize it:
template<typename T> class base;
template<> class base<first> {
public:
static first fun(const string &s1, const string &s2) {
return first(s1);
}
};
template<> class base<second> {
public:
static second fun(const string &s1, const string &s2) {
return second(s1, s2);
}
};
base<first>::fun(a, bb);
base<second>::fun(a, bb);
Demo
I am trying to wrap some templated functions into some binary functors like below. When I try to compile the code I have the error
error: no match for call to ‘(QtyAsc) (myobj&, myobj&)
I thought that being operator() in QtyAsc a function in a class, the template deduction mechanism would have worked but it seems that the compiler doesn't accept myobj classes as valid types for it.
Is it maybe because of the call to boost::bind? I was trying to provide a default implementation for the second templated argument (unfortunately I cannot use C++11 with default templated arguments).
class myobj {
public:
myobj(int val) : qty_(val) {}
int qty() { return qty_;}
private:
int qty_;
};
template<class T>
int get_quantity(const T& o) {
throw runtime_error("get_quantity<T> not implemented");
}
template<>
int get_quantity(const myobj& o) {
return o.qty();
}
struct QtyAsc {
template<class T, class QEx >
bool operator()(const T& o1, const T& o2, QEx extr = boost::bind(&get_quantity<T>,_1)) const {
if(extr(o1) < extr(o2))
return true;
return false;
}
};
int main() {
myobj t1(10),t2(20);
QtyAsc asc;
if(asc(t1,t2))
cout << "Yes" << endl;
}
If you can't use C++11, just provide an additional overload:
struct QtyAsc {
template<class T, class QEx >
bool operator()(const T& o1, const T& o2, QEx extr) const {
return extr(o1) < extr(o2);
}
template<class T>
bool operator()(const T& o1, const T& o2) const {
return operator()(o1, o2, &get_quantity<T>);
}
};
(I've omitted the unnecessary boost::bind.) Also, you will need to declare myobj::qty to be const:
int qty() const {
return qty_;
}
since you want to invoke it on const objects. (Live demo)