Overload template function in a class?

Pages: 12
Firstly, is it legal to overload a template function in a class? This is what I did

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class FILE_txt
{
public:
	FILE_txt(const char* );
	~FILE_txt(void);

	template<class T>
	void save_Data_inFile(const T);
	
	template<class T>
	void save_Data_inFile(const T []);

	std::ofstream Data_File;

private:
	std::string file_fullpath;
	
};

template<class T>
void FILE_txt::save_Data_inFile(const T data)
{
	Data_File << data << std::endl;
}

template<class T>
void FILE_txt::save_Data_inFile(const T data[3])
{
	Data_File << data[0] << " " << data[1] << " " << data[2] << std::endl;
}


main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include "FILE_txt.h"

int main()
{
	FILE_txt myfile("fullpath");
	double a[3] = {2,3,4};
	
	myfile.save_Data_inFile(a);

	std::cin.get();

	return 0;
}


But it didn't work.
Look ok except that you have to include <fstream> and implement the constructor and destructor.
@Peter87,
I did that but I didn't post the FILE_txt.cpp since it is not necessarily. My problem is that the following function is ignored

1
2
3
4
5
template<class T>
void FILE_txt::save_Data_inFile(const T data[3])
{
	Data_File << data[0] << " " << data[1] << " " << data[2] << std::endl;
}


even though I'm passing an array in the main function.
Last edited on
Maybe you didn't open the Data_File? Does Data_File.is_open() return true?
FILE_txt.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

#include "FILE_txt.h"


FILE_txt::FILE_txt(const char* file_path) : file_fullpath(file_path)
{
	// Open a file to store  data 
	 Data_File.open(file_fullpath);
	 
	 // Check whether the file failed to be open
	 if (Data_File.fail())
	{
	  std::cerr << "The file could not be opened. " << std::endl;
	}
	
}


FILE_txt::~FILE_txt(void)
{
	Data_File.close();
}
You really should post your error message.
FYI

1
2
template<class T>
void FILE_txt::save_Data_inFile(const T data[3])


actually takes a pointer. If you want it to be limited to only an array of 3 elements, you need to do this:

1
2
template<class T>
void FILE_txt::save_Data_inFile(const T (data&)[3])

Last edited on
Oh, and in response to your first question, yes, you can overload template functions.
Compiling this shows no problems with your code, just with your syntax. Look at your error messages.

If you still have problems, post them and tell us what lines they represent.
I'm not getting any error. My problem is the following function

1
2
template<class T>
void FILE_txt::save_Data_inFile(const T data[3])


Basically, I want to pass an array of double type and this function should save them in a file. If I run the following

1
2
3
4
5
6
7
8
9
10
11
int main()
{
	FILE_txt myfile("fullpath");
	double a[3] = {2,3,4};
	
	myfile.save_Data_inFile(a);

	std::cin.get();

	return 0;
}


This what I got in the file
0041FDB8

The address of the array. Why? because the function that is used is the following

1
2
3
4
5
template<class T>
void FILE_txt::save_Data_inFile(const T data)
{
	Data_File << data << std::endl;
}


Not this one

1
2
3
4
5
template<class T>
void FILE_txt::save_Data_inFile(const T data[3])
{
	Data_File << data[0] << " " << data[1] << " " << data[2] << std::endl;
}


Hopefully the problem is clear.
@AdrianH,
actually takes a pointer.


Yes I know. It is an array. This function

1
2
3
4
5
template<class T>
void FILE_txt::save_Data_inFile(const T data[3])
{
	Data_File << data[0] << " " << data[1] << " " << data[2] << std::endl;
}


should behave like the following function

1
2
3
4
void print(const double data[3])
{
	std::cout << data[0] << " " << data[1] << " " << data[2] ;
}
I think your problem is the top level const in your overloads that doesn't really have an effect.

http://www.dansaks.com/articles/2000-02%20Top-Level%20cv-Qualifiers%20in%20Function%20Parameters.pdf

Try:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class FILE_txt
{
public:
	FILE_txt(const char* );
	~FILE_txt(void);

	template<class T>
	void save_Data_inFile(T);
	
	template<class T>
	void save_Data_inFile(T*);

	std::ofstream Data_File;

private:
	std::string file_fullpath;
	
};

template<class T>
void FILE_txt::save_Data_inFile(T data)
{
	Data_File << data << std::endl;
}

template<class T>
void FILE_txt::save_Data_inFile(T* data)
{
	Data_File << data[0] << " " << data[1] << " " << data[2] << std::endl;
}



Last edited on
Ehrm, may be a little more to it than that. In a signature of void save_Data_inFile(const T*) the const isn't top level, but the other version is a better match, so supplying 3 overloads (T, T*, and const T*) may be the way to go.

[ edit: http://www.gotw.ca/publications/mill17.htm ]
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>

struct A
{
    template < typename T >
    void foo( const T& v )
    { std::cout << "A::foo( const T& v ) - v == " << v << '\n' ; }

    template < typename T, std::size_t N >
    void foo( const T(&array)[N] )
    {
        std::cout << "A::foo( const T(&array)[" << N << "] ): " ;
        for( const auto& v : array ) std::cout << v << ' ' ;
        std::cout << '\n' ;
    }

    template < typename T >
    void foo( const T array[], std::size_t n )
    {
        std::cout << "A::foo( const T array[], n ) - n == " << n << ": " ;
        for( std::size_t i = 0 ; i < n ; ++i ) std::cout << array[i] << ' ' ;
        std::cout << '\n' ;
    }
};

int main()
{
    double d[5] = { 1.1, 2.3, 3.3, 4.4, 5.5 } ;
    A a ;

    a.foo( d[3] ) ; // A::foo( const T& v ) - v == 4.4
    a.foo(d) ; // A::foo( const T(&array)[5] ): 1.1 2.3 3.3 4.4 5.5
    a.foo( d, 5 ) ; // A::foo( const T array[], n ) - n == 5: 1.1 2.3 3.3 4.4 5.5
    a.foo( +d ) ; // A::foo( const T& v ) - v == <pointer> (T is pointer to double)
}

http://ideone.com/JxJyYX
@cire,
Thank you so much. It worked but I don't think this is the problem. I think the problem is in this function

1
2
3
4
5
template<class T>
void FILE_txt::save_Data_inFile(const T& data)  // <-------- must passing by reference
{
	Data_File << data << std::endl;
}


Now if I pass an array, the compiler will not use this function
 
void FILE_txt::save_Data_inFile(const T& data)


because the name of an array is a const pointer. In this case, the compiler is able to distinguish between a mere variable and an array. Am I right?
This is what I did

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#pragma once
#include <iostream>
#include <string>
#include <fstream>


class FILE_txt
{
public:
	FILE_txt(const char* );
	~FILE_txt(void);

	template<class T>
	void save_Data_inFile(const T&);
	
	template<class T>
	void save_Data_inFile(const T []);

	std::ofstream Data_File;

private:
	std::string file_fullpath;
	
};

template<class T>
void FILE_txt::save_Data_inFile(const T& data)
{
	Data_File << data << std::endl;
}

template<class T>
void FILE_txt::save_Data_inFile(const T data[3])
{
	Data_File << data[0] << " " << data[1] << " " << data[2] << std::endl;
}
@JLBorges,
Thanks. Yes this is what I thought and it worked.
We can also use trais and SFINAE to distinguish different template parameters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <type_traits>

struct A
{
    template < typename T >
    typename std::enable_if< std::is_pointer<T>::value, void >::type
    foo( const T& v )
    { std::cout << "A::foo( const T& p ) - T is a pointer, *p == " << *v << '\n' ; }

    template < typename T >
    typename std::enable_if< !std::is_pointer<T>::value, void >::type
    foo( const T& v )
    { std::cout << "A::foo( const T& v ) - T is not a pointer, v == " << v << '\n' ; }

    template < typename T, std::size_t N >
    void foo( const T(&array)[N] )
    {
        std::cout << "A::foo( const T(&array)[" << N << "] ): " ;
        for( const auto& v : array ) std::cout << v << ' ' ;
        std::cout << '\n' ;
    }

    template < typename T >
    void foo( const T array[], std::size_t n )
    {
        std::cout << "A::foo( const T array[], n ) - n == " << n << ": " ;
        for( std::size_t i = 0 ; i < n ; ++i ) std::cout << array[i] << ' ' ;
        std::cout << '\n' ;
    }
};

int main()
{
    double d[5] = { 1.1, 2.3, 3.3, 4.4, 5.5 } ;
    A a ;

    a.foo( d[3] ) ; // A::foo( const T& v ) - T is not a pointer, v == 4.4
    a.foo(d) ; // A::foo( const T(&array)[5] ): 1.1 2.3 3.3 4.4 5.5
    a.foo( d, 5 ) ; // A::foo( const T array[], n ) - n == 5: 1.1 2.3 3.3 4.4 5.5
    a.foo( +d ) ; // A::foo( const T& p ) - T is a pointer, *p == 1.1
}

http://ideone.com/Q0wxqV
@Croco
@AdrianH,
Yes I know. It is an array. This function

1
2
3
4
5
template<class T>
void FILE_txt::save_Data_inFile(const T data[3])
{
	Data_File << data[0] << " " << data[1] << " " << data[2] << std::endl;
}


should behave like the following function

1
2
3
4
void print(const double data[3])
{
	std::cout << data[0] << " " << data[1] << " " << data[2] ;
}

Yes, that does work, but the thing is, it is equivalent to:
1
2
3
4
5
template<class T>
void FILE_txt::save_Data_inFile(const T * data)
{
	Data_File << data[0] << " " << data[1] << " " << data[2] << std::endl;
}

which is valid, but can be kinda dangerous as it allows any array length of 0 or more. If you want it to only be called for an array of 3 elements, you should do this:
1
2
3
4
5
template<class T>
void FILE_txt::save_Data_inFile(const T (&data)[3])
{
	Data_File << data[0] << " " << data[1] << " " << data[2] << std::endl;
}

Which stipulates that the array must be 3 T objects in length. No more or less.

If you want 3 or more, you would have to use SFINAE:

1
2
3
4
5
6
7
#include <type_traits>

template<class T, size_t N>
typename std::enable_if<(N>=3)>::type FILE_txt::save_Data_inFile(const T (&data)[N])
{
	Data_File << data[0] << " " << data[1] << " " << data[2] << std::endl;
}

The only problem with using this syntax is that it doesn't take a pointer, so if you dynamically allocated an array, you would have to do something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include "FILE_txt.h"

int main()
{
	FILE_txt myfile("fullpath");
	double (&array)[3] = *new double[1][3] {2,3,4};
        //double a[3] = {2,3,4};
	
	myfile.save_Data_inFile(array);

        delete[] &array; // how you would delete the dynamically allocated array
	std::cin.get();

	return 0;
}


Doing it this way keeps the array and its length from being decoupled.
Last edited on
@AdrianH,
Interesting but what is the difference between
void save_Data_inFile(const double (&data)[3])
and
void save_Data_inFile(const double data[3])

As far as I know, the name of an array is a const pointer. What is the effect of & with first prototype?
Last edited on
The second behaves as you expect, but the first says take a reference to something of type double[3] and call it data.
Pages: 12