I have this piece of code:
template<typename T>
void test(const T& p);
And I wanted to specialize template with const char* to handle string literals. So I added:
template<>
void test(const char* const& p) { std::cout << "test2\n"; }
But it turns out it doesn't work with call:
test("abc");
I know that "abc" is not technically const char pointer so I should write this:
template<size_t N>
void test(const char (&p)[N]) { std::cout << "test3\n"; }
But I don't want to differientiate between string literal and const char*, eg.:
const char* s = "abc";
test(s); test("abc"); // I want it to call the same function
So I found out that if you write "template specialization" (which is actually just overload) like this:
void test(const char* const& p) { std::cout << "test4\n"; }
it works and test("abc") and test(s) calls the same function. Why is that happening? And can I write actual template specialization for const char* and string literals because above feels like a hack - not the big one but still.
You can add another template, like so:
template<typename T>
void test(const T* p);
And then you can do:
template<>
void test(const char* p) { std::cout << "test2\n"; }
However, I don't see anything wrong with just adding a plain-old non-templated function overload to handle the const char * case:
void test(const char* p) { std::cout << "test4\n"; }
In fact, I much prefer it (because otherwise you get a separate instantiation for each different string you pass to test).
Related
I'm playing around with C++ concepts and came across an interesting problem. I have the following two custom-defined concepts:
template<typename T>
concept is_dereferencable = requires (T t) { *t; };
template<typename T>
concept is_printable = requires (T t) { std::cout << t; };
As the names suggest, the first one is used to determine if a given type can be dereferenced, while the other one to check if a type supports output operator. I also have a function template called println, which looks like this:
template<typename T>
void println(const T& t)
{
if constexpr (is_dereferencable<T>) {
if constexpr (is_printable<decltype(*t)>) {
std::cout << *t << '\n';
}
} else if constexpr (is_printable<T>) {
std::cout << t << '\n';
}
}
This prints the dereferenced value *t if and only if type T is dereference-able and the type of the dereferenced value is printable. So, for example, I can use this function template with something like an std::optional:
int main
{
std::optional<std::string> stringOpt {"My Optional String"};
::println(stringOpt);
return 0;
}
This will print My Optional String as expected. While this is nice, the function will just silently print nothing if the type of a dereferenced value of a derefernce-able is not printable. So, for a user-defined type Person, the following will just print nothing:
struct Person
{
std::string m_name;
explicit Person(const std::string& name) : m_name {name} {}
};
int main
{
std::optional<Person> personOpt {"John Doe"}
::println(personOpt);
return 0;
}
So I would like to move the above compile-time ifs to a requires clause itself in order to get compile time errors in such cases. Is there a way to achieve that? Is there a way to get the dereferenced type of a given template type T? To make it a bit clearer, I would like to have something like this:
template<typename T>
requires is_dereferencable<T> && is_printable<decltype(*T)>
void printDereferencable(const T& t)
{
std::cout << *t << '\n';
}
P.S.: I understand that I could remove the nested if and just fail upon trying to call an output operator on something that doesn't support it. However, I want to specifically move this compile-time error to the concept to get a clearer error message.
another option is put the constraint after parameters, so it has access to them.
template<typename T>
void printDereferencable(const T& t)
requires is_dereferencable<const T&> && is_printable<decltype(*t)>
{
std::cout << *t << '\n';
}
note: it should test on const T&, not T
you can use std::declval
template<typename T>
requires is_dereferencable<const T&> && is_printable<decltype(*std::declval<const T&>())>
void printDereferencable(const T& t)
{
std::cout << *t << '\n';
}
or you can just write a is_dereference_printable concept
template<typename T>
concept is_dereference_printable = requires (T t) { std::cout << *t; };
I was playing with templates to detect different string literals and I came to following code (try it with C++20):
#include <iostream>
#include <type_traits>
template <typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, const char*>, int> = 0>
void print_str(T) {
std::cout << "char* print_str\n";
}
template <typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, const char8_t*>, int> = 0>
void print_str(T) {
std::cout << "char8_t* print_str\n";
}
template <typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, const wchar_t*>, int> = 0>
void print_str(T) {
std::cout << "wchar_t* print_str\n";
}
int main() {
auto a = "hello"; // prints "char* print_str"
print_str(a);
auto b = L"hello"; // prints "wchar_t* print_str"
print_str(b);
auto c = u8"hello"; // prints "char8_t* print_str"
print_str(c);
}
I use the fact that in C++20, the u8 prefix declares char8_t type. But here are my questions:
Even though u8 prefix is from C++11, it uses char8_t only starting C++20. So how I can detect UTF-8 strings which is prefixed with u8 between C++11 and C++20?
How I can change my templates to detect both for example const char* and char* which same function? I tried to use remove_const<> but it does not work because it won't remove const from const char*.
You can't detect char8_t in languages like C++17 that did not have it. So you have to have constexpr function that detects if it is UTF-8 by examining contents of the string. Such function makes sense with char8_t also. It is simple to construct illegal UTF-8 strings using char8_t. C++17 or less did not contain such functions.
Use or? std::is_same_v<std::decay_t<T>, const char*> || std::is_same_v<std::decay_t<T>, char*>
So how I can detect UTF-8 strings which is prefixed with u8 between C++11 and C++20?
Type of string doesn't ensure encoding.
const char* can be used for utf-8 but also for LATIN-1
In the same way const wchar* is not necessary utf-16.
There is some heuristic to detect encoding.
How I can change my templates to detect both for example const char* and char* which same function? I tried to use remove_const<> but it does not work because it won't remove const from const char*.
const char * != char* const.
You can remove const from the later to obtain char*
You might do
std::is_same_v<std::remove_const_t<std::remove_pointer_t<T>>, char> && std::is_pointer_v<T>
but simply change to:
void print_str(const char*) { std::cout << "char* print_str\n"; }
void print_str(const char8_t*) { std::cout << "char8_t* print_str\n"; }
void print_str(const wchar_t*) { std::cout << "wchar_t* print_str\n"; }
If you insist with template:
template <typename T>
void print_str(const T*) {
if constexpr (std::is_same_v<char, T>) {
std::cout << "char* print_str\n";
} else if constexpr (std::is_same_v<char8_t, T>) {
std::cout << "char8_t* print_str\n";
} else if constexpr (std::is_same_v<wchar_t, T>) {
std::cout << "wchar_t* print_str\n";
}
}
I am new to templates in C++.
Can anyone explain why my specialised constructor never gets executed.
It works when I remove the const and reference operator.
#include<iostream>
#include<string>
using namespace std;
template<typename T>
class CData
{
public:
CData(const T&);
CData(const char*&);
private:
T m_Data;
};
template<typename T>
CData<T>::CData(const T& Val)
{
cout << "Template" << endl;
m_Data = Val;
}
template<>
CData<char*>::CData(const char* &Str)
{
cout << "Char*" << endl;
m_Data = new char[strlen(Str) + 1];
strcpy(m_Data, Str);
}
void main()
{
CData<int> obj1(10);
CData<char*> obj2("Hello");
}
The output is
Template
Template
Because you cannot bind "Hello" to a const char*&.
The information dyp added in comments is quite interesting:
A string literal is an array lvalue, which can be converted to a pointer prvalue. A pointer prvalue cannot bind to a non-const lvalue reference like const char* &
Which means you can actually make it work by replacing const char*& by const char* const&, or even const char* && in c++11, not sure if this is really smart in your use case though.
UPDATE I got everything wrong, rewrote the answer completely.
First, this constructor
template<>
CData<char*>::CData(const char* &Str)
is not a specialization of CData(const T&), because the Str parameter here is a non-const reference to pointer to const char. So it's a definition of non-templated constructor CData(const char*&).
Second, "Hello" has type "array of n const char" (see What is the type of string literals in C and C++?) so it can't be converted to non-const reference. This is why "Template" constructor is called.
The correct specialization is
template<>
CData<char*>::CData(char* const& Str)
That is, it accepts a const reference to char*.
And after that you should remove CData(const char*&), unless you need for example this code to compile:
const char* foo = "foo";
CData<int> obj2(foo);
So here is the code:
template<typename T>
class CData
{
public:
CData(const T&);
private:
T m_Data;
};
template<typename T>
CData<T>::CData(const T& Val)
{
....
}
template<>
CData<char*>::CData(char* const& Str)
{
....
}
// warning: deprecated conversion from string constant to 'char*'
CData<char*> obj2("Hello"); // calls CData(char* const&)
The proper way of fixing above warning is to add another specialization:
template<>
CData<const char*>::CData(const char* const& Str)
{
...
}
CData<const char*> obj2("Hello"); // calls CData(const char* const&)
I have an error
C2910: 'TEMPLATE_TEST::FuncTemplateTest::InnerFunc' : cannot be explicitly specialized,
while compiling the code below. There are two template functions, and both of them are specialized. When I remove the call to InnerFunc in the specialized outer one, everything works normally. So, where is the problem? (I'm using MS VS 2008.)
class FuncTemplateTest {
public:
template<typename T>
const int OuterFunc(const T& key) const;
private:
template<typename T>
const int InnerFunc(const T& key) const;
};
template<typename T>
inline const int FuncTemplateTest::OuterFunc(const T &key) const
{
std::cout<<"Outer template\n";
return InnerFunc(key);
}
template<>
inline const int FuncTemplateTest::OuterFunc<std::string>(const std::string &key) const
{
std::cout<<"Outer special\n" << key << '\n';
InnerFunc(key); //remove this line to compile!!!
return 1;
}
template<typename T>
inline const int FuncTemplateTest::InnerFunc(const T &key) const
{
std::cout << "Inner template\nTemplate key\n";
return 0;
}
template<>
inline const int FuncTemplateTest::InnerFunc<std::string>(const std::string &key) const
{
std::cout << key << '\n';
return 1;
}
I believe the cause of the problem is that you define an explicit specialization for InnerFunc after that particular specialization has already been used in the code for OuterFunc.
If you move the definitions for InnerFunc before the definitions for OuterFunc, you should be fine. (On GCC this indeed solved the problem.)
Separate note: The return type of your functions is const int, which is not incorrect, but also quite useless (const is ignored when fundamental data types are returned by copy).
I have a templated function which I want to specialize foo to const char[N] (hardcoded strings)
template<typename T>
const std::string foo() ;
template<typename T,int N>
const std::string foo<T[N]>() { return "T[N]"; } //this doesn't work for const char[12]
template<>
const std::string foo<const char*>() { return "Const char"; } //this doesn't work for const char[12]
template<typename T>
void someother function(T obj)
{
string s = foo<T>(); //I want to overload when T is const chat[N]
}
function("Hello World"); //When calling like this the type is const char[12]
I thought I can do something like was done Here.
But it doesn't work as well, because I'm not passing a parameter, just a templated type.
I could do it like that, but there's just no reason to pass a parameter to that function.
The example doesn't work because I'm not passing a variable. Tried a few things, but can't get it to work.
This is the only specialization I'm not able to solve. I've specialized the function for int,string and other types and they work OK.
error LNK2001: unresolved external symbol "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const __cdecl foo<char const [12]>(void)"
The first templated declaration doesn't have any code in purpose... I'm trying to get the right specialization that will be used for my case.
You have to do it in two steps. Since you can't partially specialize a function, you have to have the function call a class, which can be partially specialized. So the below will work.
#include <typeinfo>
#include <iostream>
namespace detail {
template <typename T> struct foo_helper {
static std::string helper() {
return typeid(T).name();
}
};
template <int N> struct foo_helper<const char [N]> {
static std::string helper() {
return "Const Char Array";
}
};
}
template <typename T> std::string foo(T& obj) {
return detail::foo_helper<T>::helper();
}
int main() {
std::string x;
const char c[] = "hard coded";
std::cout << foo(x) << std::endl;
std::cout << foo(c) << std::endl;
}
This calls the specialization properly for the constant string. I also changed T obj into T& obj, so g++ would pass the static strings as arrays, not pointers. For more detail on partial specialization, look at http://www.gotw.ca/publications/mill17.htm
You can't. This is impossible. You would need a partial specialization to specialize for const char(&)[N], but since you can't partially specialize functions, then it is impossible. You can always overload for it.