Background:
I'm working on a query DSL that'll parse expressions with ==
, <
, etc., and return filter objects via operator overloading.
Problem:
My template method is failing when used with a string literal. I've tried providing specific instantiations of the template taking both std::string
, and char
but neither seems to work.
The code is below. The line causing the problem in main is marked with a comment. Alternative solutions I've tried are commented out within the code.
A runnable repl of the same code can be found here.
I do know that manually wrapping the string literal with std::string("text")
will work, but I want to be able to use plain string literals, if at all possible.
#include <iostream>
template<typename T>
struct Filter;
struct Field
{
Field(const std::string &val): name(val) { }
Field(std::string &&val): name(std::move(val)) { }
std::string name;
// template <signed N>
// Filter<std::string> operator==(const char (&val) [N]);
template <typename T>
Filter<T> operator==(const T &val);
};
template <typename T>
Filter<T> Field::operator==(const T &val)
{
return Filter<T>{ *this, val, "==" };
}
// template <signed N>
// Filter<std::string> Field::operator==(const char (&val) [N])
// {
// return Filter<std::string>{ *this, std::string(val), "==" };
// }
// template <>
// Filter<std::string> Field::operator==<std::string>(const std::string &val)
// {
// return Filter<std::string>{ *this, val, "==" };
// }
template<typename T>
struct Filter
{
Field f;
T val;
std::string op;
};
int main() {
Field f1 { "field1" };
Field f2 { "field1" };
std::cout << (f1 == 1).val;
std::cout << (f1 == "Hello").val; // <--- the source of my problems
}
Issue is that c-array is not copyable, so
Filter<char [6]>{ *this, val, "==" }; // Error
Your overload is correct, but Filter
needs to reordered and defined before your operator==
overload. The overload returning Filter<T>
is dependent on T
, and so in that case Filter
's definition can be postponed. But when you return Filter<std::string>
the compiler needs an actual definition of Filter
up front.
#include <iostream>
template<typename T>
struct Filter;
struct Field
{
Field(const std::string &val): name(val) { }
Field(std::string &&val): name(std::move(val)) { }
std::string name;
template <std::size_t N> Filter<std::string> operator==(const char (&val) [N]);
template <typename T>
Filter<T> operator==(const T &val);
};
template<typename T>
struct Filter
{
Field f;
T val;
std::string op;
};
template <typename T>
Filter<T> Field::operator==(const T &val)
{
return Filter<T>{ *this, val, "==" };
}
template <std::size_t N>
Filter<std::string> Field::operator==(const char (&val) [N])
{
return Filter<std::string>{ *this, std::string(val), "==" };
}
int main() {
Field f1 { "field1" };
Field f2 { "field1" };
std::cout << (f1 == 1).val;
std::cout << (f1 == "Hello").val;
}
Demo
What you can do is specialize Filter
for the case when T
gets deduced to a char[N]
. Adding
template<std::size_t N>
struct Filter<char[N]>
{
Field f;
std::string val;
std::string op;
};
Will cause Filter<T>{ *this, val, "==" }
to call the above specialization and it will use a std::string
to store val
.
Since you have the C++17 tag, here is yet another option to solve this problem: deduction guides
#include <iostream>
template<typename T>
struct Filter;
struct Field
{
Field(const std::string &val): name(val) { }
Field(std::string &&val): name(std::move(val)) { }
std::string name;
// note the use of auto here
template <typename T>
auto operator==(const T &val);
};
template <typename T>
auto Field::operator==(const T &val)
{
// do not use Filter<T> here, or the deduction guide won't kick in
return Filter{ *this, val, "==" };
}
template<typename T>
struct Filter
{
Field f;
T val;
std::string op;
};
// ------- Deduction Guides -----------
template<typename T>
Filter(Field, T, std::string) -> Filter<T>;
// will tell the compiler to create a Filter<string> with a c-array argument
template<std::size_t N>
Filter(Field, const char(&)[N], std::string) -> Filter<std::string>;
// ------------------------------------
int main() {
Field f1 { "field1" };
Field f2 { "field1" };
std::cout << (f1 == 1).val;
// creates a Filter<string> instead of trying to
// create a Filter<const char(&)[6]> due to the deduction guide
std::cout << (f1 == "Hello").val;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With