none
Template<Typename T> Function: C3861 Error RRS feed

  • Question

  • Hi there,

    Got this code (not mine) for fn definition

    template<typename T>UInt64 GetTypeID<T *>(VMClassRegistry * registry)
    {
    
    	UInt64		result;
    
    	typedef std::remove_pointer <IsArrayType<T>::TypedArg>::type	BaseType;
    
    	if(!IsArrayType<T>::value) {
    		result = GetTypeIDFromFormTypeID(BaseType::kTypeID, registry);
    
    	} else { // Arrays are ClassInfo ptr + 1
    		result = GetTypeIDFromFormTypeID(BaseType::kTypeID, registry) | VMValue::kType_Identifier;
    
    	}
    
    	return result;
    }

    Instantiate fn in the same (header) module with this (not sure if this is best practice)

    	void PackArray(VMValue::ArrayData * data, VMClassRegistry * registry)
    
    	{
    		// Copy the contents from the reference array to the VM array
    		UInt32 i = 0;
    
    		for(typename std::vector<T>::iterator it = tList::Iterator::Begin(); it != tList::Iterator::End(); ++it, i++)
     {
    			VMValue * value = data->GetData() + i;
    
    			PackValue(value, (T*)&(*it), registry);
    
    			value->type = GetTypeID<T>(registry);
     // Always pack the type, even if empty data
    
    		}
    	}
    };

    Error (identifier not found) for GetTypeID is on this line, where registry is a local variable:

    value->type = GetTypeID<T>(registry); // Always pack the type, even if empty data

    Unable to locate much info directly regarding this: we call it as <T> but it's actually <T *>?? Perhaps the compiler is confused as much as I. Tried inserting a few keywords- different errors. Any hints for the error begone welcome,

    Thanks for reading.


    A natural, B flat, C sharp, D compile


    • Edited by LaurieSt Friday, January 31, 2020 8:30 AM
    Friday, January 31, 2020 5:25 AM

Answers

  • No, this is simply a bad function definition.

    Template function definitions do not support partial specialisation so if you have code like:

    template<typename T>
    uint64_t template_function(int param)
    {
    	return 0;
    }
    
    template<typename T>
    uint64_t template_function<T *>(int param)
    //c++ does not support partial template specialisation for a function template
    {
    	return 0;
    }

    you should be seeing errors like:

    1>C:\Users\Darran\source\repos\meh\meh\main.cpp(11,10): error C2995: 'uint64_t template_function(int)': function template has already been defined
    1>C:\Users\Darran\source\repos\meh\meh\main.cpp(5): message : see declaration of 'template_function'

    or if you use LLVM/Clang:

    <source>:10:10: error: function template partial specialization is not allowed

    uint64_t template_function<T *>(int param)

    So what do you do if you want to have partial template specialisation but in a template function? You use a template class/struct which does support partial template specialisation.

    #include <cinttypes>
    
    template<typename T>
    struct s
    {
    	static uint64_t template_function_impl(int param)
    	{
    		return 0;
    	}
    };
    
    template<typename T>
    struct s<T *>
    {
    	static uint64_t template_function_impl(int param)
    	{
    		return 0;
    	}
    };
    
    template<typename T>
    inline uint64_t template_function(int param)
    {
    	return s<T>::template_function_impl(param);
    }
    
    int main()
    {
    	template_function<int>(0);
    	template_function<int *>(0);
    	return 0;
    }

    Unfortunately this is the only way you can get partial template specialisation into template functions.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Edited by Darran Rowe Friday, January 31, 2020 10:26 AM
    • Marked as answer by LaurieSt Saturday, February 8, 2020 6:21 AM
    Friday, January 31, 2020 10:23 AM

All replies

  • Hi,

    Thank you for posting here.

    The template syntax in c++ is not just template <typename T>. 

    There're three types of template parameters:
    1,non-type template parameter;
    2,type template parameter;
    3,template template parameter.

    The typename keyword is needed for declaring type template parameter, and template and typename keyword are needed for declaring template template parameter. Given template <void (*T)(int &)>, T is declared as a non-type template parameter (a function pointer).

    And about why not just doing void PackArray(VMValue::ArrayData * data, VMClassRegistry * registry), because we have to declare T as a template parameter, otherwise it'll be just a function parameter name.

    For more details you could refer to the link:

    https://en.cppreference.com/w/cpp/language/template_parameters

    Best Regards,

    Jeanine Zhang

    Friday, January 31, 2020 10:00 AM
    Moderator
  • No, this is simply a bad function definition.

    Template function definitions do not support partial specialisation so if you have code like:

    template<typename T>
    uint64_t template_function(int param)
    {
    	return 0;
    }
    
    template<typename T>
    uint64_t template_function<T *>(int param)
    //c++ does not support partial template specialisation for a function template
    {
    	return 0;
    }

    you should be seeing errors like:

    1>C:\Users\Darran\source\repos\meh\meh\main.cpp(11,10): error C2995: 'uint64_t template_function(int)': function template has already been defined
    1>C:\Users\Darran\source\repos\meh\meh\main.cpp(5): message : see declaration of 'template_function'

    or if you use LLVM/Clang:

    <source>:10:10: error: function template partial specialization is not allowed

    uint64_t template_function<T *>(int param)

    So what do you do if you want to have partial template specialisation but in a template function? You use a template class/struct which does support partial template specialisation.

    #include <cinttypes>
    
    template<typename T>
    struct s
    {
    	static uint64_t template_function_impl(int param)
    	{
    		return 0;
    	}
    };
    
    template<typename T>
    struct s<T *>
    {
    	static uint64_t template_function_impl(int param)
    	{
    		return 0;
    	}
    };
    
    template<typename T>
    inline uint64_t template_function(int param)
    {
    	return s<T>::template_function_impl(param);
    }
    
    int main()
    {
    	template_function<int>(0);
    	template_function<int *>(0);
    	return 0;
    }

    Unfortunately this is the only way you can get partial template specialisation into template functions.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Edited by Darran Rowe Friday, January 31, 2020 10:26 AM
    • Marked as answer by LaurieSt Saturday, February 8, 2020 6:21 AM
    Friday, January 31, 2020 10:23 AM
  • @jeanine: Thanks for the hint that T in <void (*T)(int &)> is non-typed.

    template<typename T>int foo<T *>(void * bar)

    Does the above attempt fit that category?

    There's also good reference on template specialisation at cppreference, and even at Wikipedia, and there's sure to be even better coverage in a book somewhere. And yes, it is reasonable to do away with the template altogether, at least until such time the programmer wishes to expand it for later.

    @Darren: With that explanation, a book won't be required at all. Thanks!


    A natural, B flat, C sharp, D compile

    Friday, January 31, 2020 1:56 PM
  • You're really only interested in the function::result_type so there's really no need to go through the bugged path of returning a function. Just return the result type and do a decltype on that (you don't even need to define the function since you're not actually calling it.) Something like this:

    template <typename R, typename... ARGS>
    R make_func(R(*)(ARGS...));

    Then just directly use the return type:

    template <typename T>
    decltype(make_func(&VectorVolume<param_vector<T>>)) func(const T& dir) {
        return VectorVolume(dir.x, dir.y, dir.z);
    }
    Friday, January 31, 2020 2:37 PM
  • @AbigailFord: Thanks, will be looking how to apply that to the above solution.

    @Darran: The implementation went as follows:

    template<typename T>
    inline UInt64 GetTypeID(VMClassRegistry* registry);
    
    template<typename T>
    inline UInt64 GetTypeID(VMClassRegistry* registry)
    {
    	return s<T>::GetTypeID_impl(registry);
    
    }
    
    
    
    template<typename T>
    struct s
    {
    	static UInt64 GetTypeID_impl(VMClassRegistry* registry)
    
    	{
    		UInt64		result;
    
    
    		typedef std::remove_pointer <IsArrayType<T>::TypedArg>::type	BaseType;
    
    		if (!IsArrayType<T>::value) {
    			result = GetTypeIDFromFormTypeID(BaseType::kTypeID, registry);
    
    		}
    		else { // Arrays are ClassInfo ptr + 1
    			result = GetTypeIDFromFormTypeID(BaseType::kTypeID, registry) | VMValue::kType_Identifier;
    
    		}
    
    		return result;
    
    	}
    };
    
    template<typename T>
    struct s<T*>
    {
    	static UInt64 GetTypeID_impl(VMClassRegistry* registry)
    	{
    		UInt64		result;
    
    		typedef std::remove_pointer <IsArrayType<T>::TypedArg>::type	BaseType;
    		if (!IsArrayType<T>::value) {
    			result = GetTypeIDFromFormTypeID(BaseType::kTypeID, registry);
    		}
    		else { // Arrays are ClassInfo ptr + 1
    			result = GetTypeIDFromFormTypeID(BaseType::kTypeID, registry) | VMValue::kType_Identifier;
    		}
    
    		return result;
    	}
    };

    The calling method is the same.

    value->type = GetTypeID<T>(registry); // Always pack the type, even if empty data

    Forgot to mention the calling function is inside another class- as if it made any difference:

    template<typename T>
    class VMResultArray : public std::vector<T>
    {
    public:
    	void PackArray(VMValue::ArrayData * data, VMClassRegistry * registry)
    ...
    Does the above seem reasonable to you? Funny thing is, GetTypeID is still an identifier not found. The tooltip on hover of GetTypeID in the following
    template<typename T>
    inline UInt64 GetTypeID(VMClassRegistry* registry);
    reveals 

    C++ template<class T> UInt64 GetTypeID(VMClassRegistry *registry)

    A class T parameter? From reading the docs there is a difference. Do you get the same?

    Edit: A further complication is that the associated cpp for the header has these bunch of definitions:

    template <> UInt64 GetTypeID <void>(VMClassRegistry * registry)	{ return VMValue::kType_None; }
    
    template <> UInt64 GetTypeID <UInt32>(VMClassRegistry * registry)	{ return VMValue::kType_Int; }
    
    template <> UInt64 GetTypeID <SInt32>(VMClassRegistry * registry)	{ return VMValue::kType_Int; }
    
    template <> UInt64 GetTypeID <int>(VMClassRegistry * registry)	{ return VMValue::kType_Int; }
    
    template <> UInt64 GetTypeID <float>(VMClassRegistry * registry)	{ return VMValue::kType_Float; }
    
    template <> UInt64 GetTypeID <bool>(VMClassRegistry * registry)	{ return VMValue::kType_Bool; }
    And a lot more of them. Just to be absolutely sure, is there any issue of the definition  in the header being ignored in favour of one of these? Thanks.

    A natural, B flat, C sharp, D compile

    • Edited by LaurieSt Saturday, February 1, 2020 8:27 AM
    Saturday, February 1, 2020 5:54 AM
  • Forgot to mention the calling function is inside another class- as if it made any difference:

    This is irrelevant if it is able to see the definition of the function.

    Funny thing is, GetTypeID is still an identifier not found.

    This only occurs if the calling function is not able to see the function definition and any declarations. Since the intellisense doesn't use the VC compiler to parse your code then it is possible for the intellisense to see something that the VC compiler doesn't. So the implementation is perfectly fine, the problem is something that you haven't mentioned yet.

    A class T parameter? From reading the docs there is a difference. Do you get the same?

    Only for template template parameters and this is only true up to C++17. The upcoming C++20 standard makes typename and class act the same with template template parameters. Also what I pasted in my previous post is exactly what I wrote in Visual Studio:

    So since this was written to only use regular type parameters there is no issue with typename and there is no difference between typename and class.

    Just to be absolutely sure, is there any issue of the definition in the header being ignored in favour of one of these?

    Nope, identifier not found means that it just can't find the function definition or even declaration. If it was somehow finding these specialisations but not the base template then the error would be different.

    In this case you should treat the intellisense as an unreliable witness and check to see if the header that has the template can be found from the file that calls the template function manually. Take macros into account too.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Saturday, February 1, 2020 11:00 AM
  • Thanks for the heads up- it's all starting to make sense. :)  Popping the OP example back into the mixer still extrudes the C3861:

    value->type = GetTypeID<T>(registry); // Always pack the type, even if empty data

    Now consider the following decl.

    template <> UInt64 GetTypeID <T>(VMClassRegistry * registry);

    This produces a syntax error as T is not an implicit type. But the above C3861 is begone with 

    value->type = GetTypeID<>(registry);

    Would the empty <> suffice as a <*> where * represents all the included defined types? Presumably the intention of all this is to avoid iterating through the types. Consider this next instantiation:

    dst->type = GetTypeID<VMResultArray<T>>(registry);

    Note the previously mentioned VMResultArray is the container class for PackArray. There are specialisations for it in the same module:

    template <> UInt64 GetTypeID <void>(VMClassRegistry * registry);
    template <> UInt64 GetTypeID <UInt32>(VMClassRegistry * registry);
    template <> UInt64 GetTypeID <SInt32>(VMClassRegistry * registry);
    template <> UInt64 GetTypeID <int>(VMClassRegistry * registry);
    template <> UInt64 GetTypeID <float>(VMClassRegistry * registry);
    template <> UInt64 GetTypeID <bool>(VMClassRegistry * registry);
    template <> UInt64 GetTypeID <BSFixedString>(VMClassRegistry * registry);

    Interesting thing is the following specialisation plays nice with the compiler only if the decl. is placed in the function before the call thus:

    template <> UInt64 GetTypeID <VMResultArray<T>>(VMClassRegistry * registry);
    dst->type = GetTypeID<VMResultArray<T>>(*registry);

    If, instead, this

    template <> UInt64 GetTypeID <VMResultArray<T>>(VMClassRegistry * registry);

    is placed with the other specs in the header, we get a C2065 undeclared identifier of "T".
    The patched up fixture looks rather inelegant- would it qualify as inefficient code?

    Thanks again.


    A natural, B flat, C sharp, D compile



    • Edited by LaurieSt Sunday, February 2, 2020 4:46 AM
    Saturday, February 1, 2020 1:00 PM