c++ : Universal getter for class - c++

I kind of need help! I want to define a template method for my class to access its private fields. Here is my code:
#include <string>
#include <vector>
using namespace std;
class ex
{
public:
ex(string pegah_,int amin_):pegah(pegah_),amin(amin_){}
template<typename T>
T get_field(){
if(is_same<T,string>::value)
return pegah;
else if(is_same<T,int> ::value)
return amin;
}
private:
string pegah;
int amin;
};
int main(void)
{
string i = "salam";
int o=10;
ex y(i,o);
y.get_field<string>();
}
as you see I want to use just one function. But I keep getting this error:
test.cpp: In instantiation of ‘T ex::get_field() [with T = std::basic_string<char>]’:
test.cpp:30:21: required from here
test.cpp:15:8: error: invalid conversion from ‘int’ to ‘const char*’ [-fpermissive]
return amin;
^
In file included from /usr/include/c++/4.8/string:52:0,
from test.cpp:1:
/usr/include/c++/4.8/bits/basic_string.h:490:7: error: initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc());
can anyone help?

Instead you could lay your code out like this:
template<typename T> T get_field();
// outside the class:
template<> inline int ex::get_field<int>() { return amin; }
template<> inline string ex::get_field<string>() { return pegah; }
As you have it now, all branches of the if..else must compile.

Basically you have three options to do it.
First using explicit specialization of template member function.
class Foo {
public:
template <typename T>
T get () const;
private:
std::string str {"XXX"};
int value {42};
};
template <>
inline std::string Foo::get () const {
return str;
}
template <>
inline int Foo::get () const {
return value;
}
Second one is to use helper function with different parameters type.
class Foo2 {
public:
template <typename T>
T get () const {
return get_helper (typename std::is_same<T, std::string>::type {});
}
private:
std::string get_helper (std::true_type) const {
return str;
}
int get_helper (std::false_type) const {
return value;
}
private:
std::string str {"YYY"};
int value {44};
};
Third option is to use SFINAE.
class Foo3 {
public:
template <typename T>
typename std::enable_if<std::is_same<T, std::string>::value, T>::type get () const {
return str;
}
template <typename T>
typename std::enable_if<std::is_same<T, int>::value, T>::type get () const {
return value;
}
private:
std::string str {"ZZZ"};
int value {45};
};
and usage would be like:
template <typename T>
void show (T v) {
std::cout << v << std::endl;
}
Foo f1;
show (f1.get<std::string> ());
show (f1.get<int> ());
Foo2 f2;
show (f2.get<std::string> ());
show (f2.get<int> ());
Foo3 f3;
show (f3.get<std::string> ());
show (f3.get<int> ());
Second option is helpful when you want to distinguish between two types. If you have more getters, then probably you will need to use first or third option.

I think it is better you define a getter and setter for each field. That is a better approach. It's easier to read and to understand and you achieve the same as with the template technique.
Explanation of your code:
It does not compile because of type checking. Template functions are generated when used in C++11. You use it with template parameter string so the function is generated. The problem is that you generate a function that returns T as a string, but you have code in your function that returns int (variable amin). Generate the function in your mind like so for T equals string:
string get_field(){
if(is_same<string,string>::value)
return pegah; // OK
else if(is_same<string,int> ::value)
return amin; // NOT OK, amin is of type int
}
One solution is that of M.M, it's called specialization. You specialize a template for (a) specific argument(s). And there are also other answers coming up.
I do not recommend that, because you finally do nothing else but generating getter functions for each variable in a specialized template. You could as well just have written:
string get_pegah(){ return pegah; }
int get_amin() { return amin; }
Easier to read, to maintain and straight forward. And more efficient I think.
as you see I want to use just one function
You don't really. You either call get_field<string> or get_field<int> and when called the appropriate function would be generated; either with T=string, T=int or both (depending on your use case). Though as you have learned by now, it's an error to do so in that case.
What you probably meant was that you want to have one function definition to do what you want. I don't think that is possible.

Related

what is the way to remove the first element from a std::span<T>?

when reading the document of std::span, I see there is no method to remove the first element from the std::span<T>.
Can you suggest a way to solve my issue?
The large picture of my problem(I asked in another question: How to instantiatiate a std::basic_string_view with custom class T, I got is_trivial_v<_CharT> assert error) is that I would like to have a std::basic_string_view<Token>, while the Token is not a trivial class, so I can't use std::basic_string_view, and someone suggested me to use std::span<Token> instead.
Since the basic_string_view has a method named remove_prefix which remove the first element, while I also need such kinds of function because I would like to use std::span<Token> as a parser input, so the Tokens will be matched, and consumed one by one.
Thanks.
EDIT 2023-02-04
I try to derive a class named Span from std::span, and add the remove_prefix member function, but it looks like I still have build issues:
#include <string_view>
#include <vector>
#include <span>
// derived class, add remove_prefix function to std::span
template<typename T>
class Span : public std::span<T>
{
public:
// Inheriting constructors
using std::span<T>::span;
// add a public function which is similar to std::string_view::remove_prefix
constexpr void remove_prefix(std::size_t n) {
*this = subspan(n);
}
};
struct Token
{
Token(){};
Token(const Token& other)
{
lexeme = other.lexeme;
type = other.type;
}
std::string_view lexeme;
int type;
// equal operator
bool operator==(const Token& other)const {
return (this->lexeme == other.lexeme) ;
}
};
template <typename T>
struct Viewer;
template <>
struct Viewer<Token>
{
using type = Span<Token>; // std::span or derived class
};
template <>
struct Viewer<char>
{
using type = std::string_view;
};
template <typename T> using ViewerT = typename Viewer<T>::type;
template <typename T>
class Parser
{
using v = ViewerT<T>;
};
// a simple parser demo
template <typename Base, typename T>
struct parser_base {
using v = ViewerT<T>;
constexpr auto operator[](v& output) const noexcept;
};
template<typename T>
struct char_ final : public parser_base<char_<T>, T> {
using v = ViewerT<T>;
constexpr explicit char_(const T ch) noexcept
: ch(ch)
{}
constexpr inline bool visit(v& sv) const& noexcept {
if (!sv.empty() && sv.front() == ch) {
sv.remove_prefix(1);
return true;
}
return false;
}
private:
T ch;
};
template <typename Parser, typename T>
constexpr bool parse(Span<T> &input, Parser const& parser) noexcept {
return parser.visit(input);
}
int main()
{
Token kw_class;
kw_class.lexeme = "a";
std::vector<Token> token_stream;
token_stream.push_back(kw_class);
token_stream.push_back(kw_class);
token_stream.push_back(kw_class);
Span<Token> token_stream_view{&token_stream[0], 3};
auto p = char_(kw_class);
parse(token_stream_view, p);
return 0;
}
The build error looks like below:
[ 50.0%] g++.exe -Wall -std=c++20 -fexceptions -g -c F:\code\test_crtp_twoargs\main.cpp -o obj\Debug\main.o
F:\code\test_crtp_twoargs\main.cpp: In member function 'constexpr void Span<T>::remove_prefix(std::size_t)':
F:\code\test_crtp_twoargs\main.cpp:52:17: error: there are no arguments to 'subspan' that depend on a template parameter, so a declaration of 'subspan' must be available [-fpermissive]
52 | *this = subspan(n);
| ^~~~~~~
F:\code\test_crtp_twoargs\main.cpp:52:17: note: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)
F:\code\test_crtp_twoargs\main.cpp: In instantiation of 'constexpr void Span<T>::remove_prefix(std::size_t) [with T = Token; std::size_t = long long unsigned int]':
F:\code\test_crtp_twoargs\main.cpp:113:29: required from 'constexpr bool char_<T>::visit(v&) const & [with T = Token; v = Span<Token>]'
F:\code\test_crtp_twoargs\main.cpp:125:24: required from 'constexpr bool parse(Span<T>&, const Parser&) [with Parser = char_<Token>; T = Token]'
F:\code\test_crtp_twoargs\main.cpp:141:10: required from here
F:\code\test_crtp_twoargs\main.cpp:52:24: error: 'subspan' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
52 | *this = subspan(n);
| ~~~~~~~^~~
F:\code\test_crtp_twoargs\main.cpp:52:24: note: declarations in dependent base 'std::span<Token, 18446744073709551615>' are not found by unqualified lookup
F:\code\test_crtp_twoargs\main.cpp:52:24: note: use 'this->subspan' instead
F:\code\test_crtp_twoargs\main.cpp:52:15: error: no match for 'operator=' (operand types are 'Span<Token>' and 'std::span<Token, 18446744073709551615>')
52 | *this = subspan(n);
| ~~~~~~^~~~~~~~~~~~
F:\code\test_crtp_twoargs\main.cpp:44:7: note: candidate: 'constexpr Span<Token>& Span<Token>::operator=(const Span<Token>&)'
44 | class Span : public std::span<T>
| ^~~~
F:\code\test_crtp_twoargs\main.cpp:44:7: note: no known conversion for argument 1 from 'std::span<Token, 18446744073709551615>' to 'const Span<Token>&'
F:\code\test_crtp_twoargs\main.cpp:44:7: note: candidate: 'constexpr Span<Token>& Span<Token>::operator=(Span<Token>&&)'
F:\code\test_crtp_twoargs\main.cpp:44:7: note: no known conversion for argument 1 from 'std::span<Token, 18446744073709551615>' to 'Span<Token>&&'
Any idea on how to fix this issue?
Also, I don't know how to make a general parse function:
template <typename Parser, typename T>
constexpr bool parse(Span<T> &input, Parser const& parser) noexcept {
return parser.visit(input);
}
Currently, the first argument of the parse should be a Viewer like type?
EDIT2023-02-05
Change the function as below, the above code can build correctly. This is from Benjamin Buch's answer.
constexpr void remove_prefix(std::size_t n) {
auto& self = static_cast<std::span<T>&>(*this);
self = self.subspan(n);
}
There is still one thing remains: How to generalize the parse function to accept both input types of std::string_view and Span<Token>?
If I change the parse function to this:
template <typename Parser, typename T>
constexpr bool parse(ViewerT<T> &input, Parser const& parser) noexcept {
return parser.visit(input);
}
I got such compile error:
[ 50.0%] g++.exe -Wall -std=c++20 -fexceptions -g -c F:\code\test_crtp_twoargs\main.cpp -o obj\Debug\main.o
F:\code\test_crtp_twoargs\main.cpp: In function 'int main()':
F:\code\test_crtp_twoargs\main.cpp:143:24: error: no matching function for call to 'parse(Span<Token>&, char_<Token>&)'
143 | bool result = parse(token_stream_view, p);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~
F:\code\test_crtp_twoargs\main.cpp:125:16: note: candidate: 'template<class Parser, class T> constexpr bool parse(ViewerT<T>&, const Parser&)'
125 | constexpr bool parse(ViewerT<T> &input, Parser const& parser) noexcept {
| ^~~~~
F:\code\test_crtp_twoargs\main.cpp:125:16: note: template argument deduction/substitution failed:
F:\code\test_crtp_twoargs\main.cpp:143:24: note: couldn't deduce template parameter 'T'
143 | bool result = parse(token_stream_view, p);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~
Any ideas?
Thanks.
BTW: I have to explicitly instantiation of the parse function call like:
bool result = parse<decltype(p), Token>(token_stream_view, p);
to workaround this issue.
Call subspan with 1 as only (template) argument to get a new span, which doesn't contain the first element.
If you use a span with a static extend, you need a new variable because the data type changes by subspan.
#include <string_view>
#include <iostream>
#include <span>
int main() {
std::span<char const, 12> text_a("a test-span");
std::cout << std::string_view(text_a) << '\n';
std::span<char const, 10> text_b = text_a.subspan<2>();
std::cout << std::string_view(text_b) << '\n';
}
If you have a dynamic extend, you can assign the result to the original variable.
#include <string_view>
#include <iostream>
#include <span>
int main() {
std::span<char const> text("a test-span");
std::cout << std::string_view(text) << '\n';
text = text.subspan(2);
std::cout << std::string_view(text) << '\n';
}
The implementation of a modifying inplace subspan version is only possible for spans with a dynamic extend. It can be implemented as a free function.
#include <string_view>
#include <iostream>
#include <span>
template <typename T>
constexpr void remove_front(std::span<T>& self, std::size_t const n) noexcept {
self = self.subspan(n);
}
int main() {
std::span<char const> text("a test-span");
std::cout << std::string_view(text) << '\n';
remove_front(text, 2);
std::cout << std::string_view(text) << '\n';
}
You can use your own spans derived from std::span if you prefer the dot-call.
#include <string_view>
#include <iostream>
#include <span>
template <typename T>
struct my_span: std::span<T> {
using std::span<T>::span;
constexpr void remove_front(std::size_t const n) noexcept {
auto& self = static_cast<std::span<T>&>(*this);
self = self.subspan(n);
}
};
int main() {
my_span<char const> my_text("a test-span");
std::cout << std::string_view(my_text) << '\n';
my_text.remove_front(2);
std::cout << std::string_view(my_text) << '\n';
}
You can also write a wrapper class to call via dot syntax. This way you can additionally implement cascadable modification calls by always returning the a reference modifier class.
#include <string_view>
#include <iostream>
#include <span>
template <typename T>
class span_modifier {
public:
constexpr span_modifier(std::span<T>& span) noexcept: span_(span) {}
constexpr span_modifier& remove_front(std::size_t const n) noexcept {
span_ = span_.subspan(n);
return *this;
}
private:
std::span<T>& span_;
};
template <typename T>
constexpr span_modifier<T> modify(std::span<T>& span) noexcept {
return span;
}
int main() {
std::span<char const> text("a test-span");
std::cout << std::string_view(text) << '\n';
modify(text).remove_front(2).remove_front(5);
std::cout << std::string_view(text) << '\n';
}
Note I use the template function modify to create an object of the wrapper class, because the names of classes cannot be overloaded. Therefore class names should always be a bit more specific. The function modify can also be overloaded for other data types, which then return a different wrapper class. This results in a simple intuitive and consistent interface for modification wrappers.
You can write remove_prefix of your version,
template <typename T>
constexpr void remove_prefix(std::span<T>& sp, std::size_t n) {
sp = sp.subspan(n);
}
Demo

Is there an analogue of keyword "where" from C# in C++?

I need to create a template class in C++. I need to make sure that the type for the template parameter will be a class with 1 int field and 1 string field (there can be more fields, but these are mandatory).
For example, in C# I could define an interface with methods or properties, like this:
interface MyInterface {
int GetSomeInteger();
string GetSomeString();
}
and then I could use it in my template class:
class MyClass<T> where T: MyInterface {}
Is there any way to do something like this in C++?
C++20 offers you the closest solution to C#:
#include <concepts>
template <class T>
concept MyInterface = requires(T x)
{
{ x.GetSomeInteger() } -> std::same_as<int>;
};
And then:
template <MyInterface T>
struct MyClass
{
// ...
};
The most common way of doing this in current versions of C++ is a technique known as "duck-typing".
It simply involves just using T as if it implements the interface and let the compiler fail if you use the class with an incompatible type.
template<typename T>
class MyClass<T> {
int foo() {
T val;
return val.GetSomeInteger();
}
};
class Valid {
public:
int GetSomeInteger() {return 0;}
};
class Invalid {
};
int main() {
// works fine
MyClass<Valid> a;
a.foo();
// fails to compile
MyClass<Invalid> b;
b.foo();
}
Mind you, there ARE ways of enforcing this a bit more formally, but the amount of code involved is often not worth the benefit.
C++20 has concepts. Some compilers already support them. For example the following with gcc (trunk) -std=c++2a -fconcepts:
#include <string>
#include <iostream>
#include <concepts>
template<typename T>
concept HasGetIntAndString = requires(T& a) {
{ a.GetSomeInteger() } -> std::same_as<int>;
{ a.GetSomeString() } -> std::same_as<std::string>;
};
template <HasGetIntAndString T>
void bar(const T& t){
std::cout << t.GetSomeInteger() << " " << t.GetSomeString();
}
struct foo {
int GetSomeInteger() const { return 42; }
std::string GetSomeString() const { return "some"; }
};
struct foo_not {
std::string GetSomeInteger() { return "some"; }
int GetSomeString() { return 42; }
};
int main(){
bar( foo{});
bar( foo_not{});
}
results in:
<source>: In function 'int main()':
<source>:28:19: error: use of function 'void bar(const T&) [with T = foo_not]' with unsatisfied constraints
28 | bar( foo_not{});
| ^
<source>:12:6: note: declared here
12 | void bar(const T& t){
| ^~~
<source>:12:6: note: constraints not satisfied
<source>: In instantiation of 'void bar(const T&) [with T = foo_not]':
<source>:28:19: required from here
<source>:6:9: required for the satisfaction of 'HasGetIntAndString<T>' [with T = foo_not]
<source>:6:30: in requirements with 'T& a' [with T = foo_not]
<source>:7:23: note: 'a.GetSomeInteger()' does not satisfy return-type-requirement
7 | { a.GetSomeInteger() } -> std::same_as<int>;
| ~~~~~~~~~~~~~~~~^~
<source>:8:22: note: 'a.GetSomeString()' does not satisfy return-type-requirement
8 | { a.GetSomeString() } -> std::same_as<std::string>;
| ~~~~~~~~~~~~~~~^~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
Live Demo
Before C++20 you can use SFINAE. However, often it is simpler and more appropriate to not restrict the tempalte parameter more than necessary. If the template does call T::GetSomeInteger() but the type T has no such method, the template will already fail to compile without taking any further measures. SFINAE is mainly to provide nicer error messages.

operator== does not compile if I include <iostream>

The following code compiles perfectly if:
I don't include <iostream> or
I name operator== as alp::operator==.
I suppose there is a problem with <iostream> and operator==, but I don't know what.
I compile the code with gcc 7.3.0, clang++-6.0 and goldbolt. Always the same error.
The problem is that the compiler is trying to cast the parameters of operator== to const_iterator, but why? (I suppose the compiler doesn't see my version of operator==, and looks for other versions).
#include <vector>
#include <iostream> // comment and compile
namespace alp{
template <typename It_base>
struct Iterator {
using const_iterator = Iterator<typename It_base::const_iterator>;
operator const_iterator() { return const_iterator{}; }
};
template <typename It_base>
bool operator==(const Iterator<It_base>& x, const Iterator<It_base>& y)
{ return true;}
}// namespace
struct Func{
int& operator()(int& p) const {return p;}
};
template <typename It, typename View>
struct View_iterator_base{
using return_type = decltype(View{}(*It{}));
using const_iterator =
View_iterator_base<std::vector<int>::const_iterator, Func>;
};
using view_it =
alp::Iterator<View_iterator_base<std::vector<int>::iterator, Func>>;
int main()
{
view_it p{};
view_it z{};
bool x = operator==(z, p); // only compiles if you remove <iostream>
bool y = alp::operator==(z,p); // always compile
}
Error message:
yy.cpp: In instantiation of ‘struct View_iterator_base<__gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func>’:
yy.cpp:9:73: required from ‘struct alp::Iterator<View_iterator_base<__gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func> >’
yy.cpp:44:29: required from here
yy.cpp:28:42: error: no match for call to ‘(Func) (const int&)’
using return_type = decltype(View{}(*It{}));
~~~~~~^~~~~~~
yy.cpp:22:10: note: candidate: int& Func::operator()(int&) const <near match>
int& operator()(int& p) const {return p;}
^~~~~~~~
yy.cpp:22:10: note: conversion of argument 1 would be ill-formed:
yy.cpp:28:42: error: binding reference of type ‘int&’ to ‘const int’ discards qualifiers
using return_type = decltype(View{}(*It{}));
~~~~~~^~~~~~~
I've made a more minimal test case here: https://godbolt.org/z/QQonMG .
The relevant details are:
A using type alias does not instantiate a template. So for example:
template<bool b>
struct fail_if_true {
static_assert(!b, "template parameter must be false");
};
using fail_if_used = fail_if_true<true>;
will not cause a compile time error (if fail_if_used isn't used)
ADL also inspects template parameter classes. In this case, std::vector<int>::iterator is __gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func>, which has a std::vector<int> in it's template. So, operator== will check in the global namespace (always), alp (As alp::Iterator is in alp), __gnu_cxx and std.
Your View_iterator_base::const_iterator is invalid. View_iterator_base::const_interator::result_type is defined as decltype(Func{}(*std::vector<int>::const_iterator{})). std::vector<int>::const_iterator{} will be a vectors const iterator, so *std::vector<int>::const_iterator{} is a const int&. Func::operator() takes an int&, so this means that the expression is invalid. But it won't cause a compile time error if not used, for the reasons stated above. This means that your conversion operator is to an invalid type.
Since you don't define it as explicit, the conversion operator (To an invalid type) will be used to try and match it to the function parameters if they don't already match. Obviously this will finally instantiate the invalid type, so it will throw a compile time error.
My guess is that iostream includes string, which defines std::operator== for strings.
Here's an example without the std namespace: https://godbolt.org/z/-wlAmv
// Avoid including headers for testing without std::
template<class T> struct is_const { static constexpr const bool value = false; } template<class T> struct is_const<const T> { static constexpr const bool value = true; }
namespace with_another_equals {
struct T {};
bool operator==(const T&, const T&) {
return true;
}
}
namespace ns {
template<class T>
struct wrapper {
using invalid_wrapper = wrapper<typename T::invalid>;
operator invalid_wrapper() {}
};
template<class T>
bool operator==(const wrapper<T>&, const wrapper<T>&) {
return true;
}
}
template<class T>
struct with_invalid {
static_assert(!is_const<T>::value, "Invalid if const");
using invalid = with_invalid<const T>;
};
template<class T>
void test() {
using wrapped = ns::wrapper<with_invalid<T>>;
wrapped a;
wrapped b;
bool x = operator==(a, b);
bool y = ns::operator==(a, b);
}
template void test<int*>();
// Will compile if this line is commented out
template void test<with_another_equals::T>();
Note that just declaring operator const_iterator() should instantiate the type. But it doesn't because it is within templates. My guess is that it is optimised out (where it does compile because it's unused) before it can be checked to show that it can't compile (It doesn't even warn with -Wall -pedantic that it doesn't have a return statement in my example).

Is this a legitimate method for making sure implicit type conversion does not happen

Is this a legitimate method for making sure implicit type conversion does not happen?
#include <string>
#include <iostream>
void func(std::string s)
{
std::cout << "Thanks for the string\n";
}
template<class T>
void func(T)=delete;
int main()
{
func("test1");
// str.cc: In function ‘int main()’:
// str.cc:13:16: error: use of deleted function ‘void func(T) [with T = const char*]’
// func("test1");
// ^
// str.cc:9:6: error: declared here
// void func(T)=delete;
// ^
//
func(std::string("test2"));
return 0;
}
Looks good to me.
It does the same thing to answers.
Yes, that method ensures that implicit conversions are disallowed. However, it also means that this property does not arise from the definition of of void func(string) alone. So, in order to clarify this to readers, you could make it more self-contained as follows:
template <typename T, typename U> using RequireExplicit = enable_if_t<is_same<T, U>>;
template <typename T, typename = RequireExplicit<T, string>>
void func(T){}

T = char can't be deduced for std::basic_string<T> foo = "foo"?

Question:
In the code below, template argument type deduction seems to fail for the first sample, but not for the second sample. I don't understand why the first sample fails to deduce T = char. I would think that T can be deduced when converting from "foo" to std::bacis_string<T>, but even if that didn't work, I provide the second function argument which, I would think, would clearly constrain T to char. Why does it fail?
Does not work:
#include <iostream>
#include <string>
template <typename T>
void print(const std::basic_string<T>& a, const std::basic_string<T>& b)
{
std::cout << a << b << std::endl;
}
int main()
{
std::string bar = "bar";
print("foo", bar);
}
Error:
string.cpp:14:5: error: no matching function for call to 'print'
print("foo", bar);
^~~~~
string.cpp:6:6: note: candidate template ignored: could not match
'basic_string<type-parameter-0-0, char_traits<type-parameter-0-0>,
allocator<type-parameter-0-0> >' against 'char const[4]'
void print(const std::basic_string<T>& a, const std::basic_string<T>& b)
^
1 error generated.
Works:
#include <iostream>
#include <string>
template <typename T>
void print(const std::basic_string<T>& a, const std::basic_string<T>& b)
{
std::cout << a << b << std::endl;
}
int main()
{
std::string foo = "foo";
std::string bar = "bar";
print(foo, bar);
}
The problem is a conversion is required here. To deduce T, the compiler would have to inspect all possible instantiations of std::basic_string and see which of them can be constructed from a const char* (or actually const char (&)[4]). That's of course not possible, as there's infinitely many of them. The reason why it has to inspect all and cannot just scan the primary template definition for constructors taking const char* or const char(&)[4] is that for some T, std::basic_string<T> could be partially or completely specialised, and the members of those specialisations have no relationship to the members of the primary template.
Here's the short version of an answer.
The compiler has char const[] and is looking to convert that to std::basic_string<T>. How does it work out what T is? You know that you want to match T = char but the compiler does not know that.
It could look for a constructor basic_string<T>(char const *), for example. Even if that exists, it still does not say what T should be.
The compiler doesn't iterate over all possible typenames it knows about and attempt basic_string<T> for each one, and then see if there is a matching constructor.
Similar example:
template<typename T>
struct Foo
{
Foo(T t) {}
};
int main()
{
Foo(0); // error, can't deduce Foo<int>
}