Automatically convert const char [] to const char * in template functions

I'm having problems with templates, so if you try to give a template function with a string argument, the compiler interprets "Hello World" as const char [12]. I would like it to be const char *.

I can "work around" the problem by statically casting each line in "const char *", but since I'm trying to use this as part of a logging system, making it simple is a big goal.

Since it’s hard to explain what I mean, I came up with a simple player. You will see that the last line of the main function does not compile.

Any help would be greatly appreciated.

#include <string> // Trivial base class so we can use polymorphism class StoreItemsBase { public: StoreItemsBase() {} }; // Example of a trivial Templated class to hold some 3 items. // Intent to have similar classes to hold 4,5..n items template <typename T1, typename T2, typename T3> class Store3Items : public StoreItemsBase { public: Store3Items(const T1& t1, const T2& t2, const T3& t3) : StoreItemsBase(), mT1(t1), mT2(t2), mT3(t3) {} private: T1 mT1; T2 mT2; T3 mT3; }; // Function to create a pointer to our object with added id // There would be similar CreateHolderFunctions for 4,5..n items template <typename T1, typename T2, typename T3> StoreItemsBase* CreateHolder(const T1& t1, const T2& t2, const T3& t3) { return new Store3Items<T1, T2, T3>(t1, t2, t3); } int main() { int testInt=3; double testDouble=23.4; const std::string testStr("Hello World"); StoreItemsBase* Ok1 = CreateHolder(testInt, testDouble, testStr); StoreItemsBase* Ok2 = CreateHolder(testDouble, testStr, testInt); StoreItemsBase* Ok3 = CreateHolder(testStr, static_cast<const char*>("Hello there"), testInt); // If you try a standard string, it compiler complains // Although I could surround all my strings with the static cast, what I am looking for is a way // to for the CreateHolder function to do the work for me StoreItemsBase* NotOk4 = CreateHolder(testStr, "Hello World", testInt); // Free our objects not shown in the example } 

Compiler error: example.cpp: In the constructor "Store3Items :: Store3Items (const T1 &, const T2 &, const T3 &) [with T1 = std :: basic_string, T2 = char [12], T3 = int]: example. cpp: 50: 50: created from 'StoreItemsBase * CreateHolder (const T1 &, const T2 &, const T3 &) [with T1 = std :: basic_string, T2 = char [12], T3 = int] example.cpp: 65 : 74: created here example.cpp: 21:11: error: array used as initializer

+4
source share
2 answers

You can use metafunction to convert the types passed as an argument to your templates. Any array of characters will be converted to char* :

 template< typename T > struct transform { typedef T type; }; template< std::size_t N > struct transform< char[N] > { typedef char* type; }; template< std::size_t N > struct transform< const char[N] > { typedef const char* type; }; 

Then, instead of using Tn you can use typename transform< Tn >::type .

Update: if you are working in C ++ 11 , then std::decay already does what you want.

+5
source

Try changing the template arguments to const T1 t1, const T2 t2, const T3 t3. It will be less productive, but it compiles

Solving function arguments based on template arguments is often difficult. You can try this to sign the constructor class. I used the specialized specialization of the arg_type class (non-standard) to make sure that all argument types that are not constant pointers are passed by the constant ref and all const pointers are passed as constant pointers.

Also, do not forget that a virtual destructor in your base class may happen or something bad :)

 #include <string> // Trivial base class so we can use polymorphism class StoreItemsBase { public: StoreItemsBase() {} virtual ~StoreItemsBase() {} }; template <typename TYPE> class arg_type { public: typedef const TYPE& type; }; template <typename TYPE> class arg_type<const TYPE*> { public: typedef const TYPE* type; }; // Example of a trivial Templated class to hold some 3 items. // Intent to have similar classes to hold 4,5..n items template <typename T1, typename T2, typename T3> class Store3Items : public StoreItemsBase { typedef typename arg_type<T1>::type arg1; typedef typename arg_type<T2>::type arg2; typedef typename arg_type<T3>::type arg3; public: Store3Items(arg1 t1, arg2 t2, arg3 t3) : StoreItemsBase(), mT1(t1), mT2(t2), mT3(t3) {} private: T1 mT1; T2 mT2; T3 mT3; }; // Function to create a pointer to our object with added id // There would be similar CreateHolderFunctions for 4,5..n items template <typename T1, typename T2, typename T3> StoreItemsBase* CreateHolder(const T1 t1, const T2 t2, const T3 t3) { return new Store3Items<T1, T2, T3>(t1, t2, t3); } int main() { int testInt=3; double testDouble=23.4; const std::string testStr("Hello World"); StoreItemsBase* Ok1 = CreateHolder(testInt, testDouble, testStr); StoreItemsBase* Ok2 = CreateHolder(testDouble, testStr, testInt); StoreItemsBase* Ok3 = CreateHolder(testStr, static_cast<const char*>("Hello there"), testInt); // If you try a standard string, it compiler complains // Although I could surround all my strings with the static cast, what I am looking for is a way // to for the CreateHolder function to do the work for me StoreItemsBase* NotOk4 = CreateHolder(testStr, "Hello World", testInt); // Free our objects not shown in the example } 

Keep in mind that your class will store the original const char * internally (without std :: string), so make sure the scope of this string will be longer than the pointer you are storing, Permanent strings, as in your example, are good because they live forever.

0
source

Source: https://habr.com/ru/post/1413076/


All Articles