Template specialization with empty definition

partial template specialization c++
template specialization in cpp file
template function specialization
c template specialization multiple types
template parameters not deducible in partial specialization
non-type partial specialization is not allowed
c++ template subclass specialization
c++ template class definition

I made a template function for initializing a chrono::time_point from a number. I have succeeded so far but encountered an issue which I do not fully understand. Two minimal examples of my code are given below.

Below code fails to compile with the following error:

/usr/include/c++/7/chrono:616:14: note:   no known conversion for argument 1 from ‘const double’ to ‘const std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<double> >&’
/usr/include/c++/7/chrono:616:14: note: candidate: constexpr std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<double> >::time_point(std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<double> >&&)
/usr/include/c++/7/chrono:616:14: note:   no known conversion for argument 1 from ‘const double’ to ‘std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<double> >&&’
#include <iostream>
#include <chrono>

namespace yv {
using clock_t = std::chrono::system_clock;
using duration_t = std::chrono::duration<double>;
using time_t = std::chrono::time_point<clock_t, duration_t>;

namespace fromnumber {
template<class T, class T_time> T_time time(T const& timestamp) {
    return T_time(timestamp);
}
// No specialization

}; // end namespace fromnumber
}; // end namespace yv


int main()
{


    using namespace yv;
    using namespace std;

    yv::time_t t0 = yv::fromnumber::time<double, yv::time_t>(0.0);
    yv::time_t t1 = yv::fromnumber::time<double, yv::time_t>(1548675254.0);

    return 0;
}

However, when I add a template specialization with empty definition it does compile.

#include <iostream>
#include <chrono>

namespace yv {
using clock_t = std::chrono::system_clock;
using duration_t = std::chrono::duration<double>;
using time_t = std::chrono::time_point<clock_t, duration_t>;

namespace fromnumber {
template<class T, class T_time> T_time time(T const& timestamp) {
    return T_time(timestamp);
}

template<> std::chrono::time_point<clock_t, duration_t> time(double const&) {
// EMPTY
}

}; // end namespace fromnumber
}; // end namespace yv


int main()
{


    using namespace yv;
    using namespace std;

    yv::time_t t0 = yv::fromnumber::time<double, yv::time_t>(0.0);
    yv::time_t t1 = yv::fromnumber::time<double, yv::time_t>(1548675254.0);

    return 0;
}

The specialization has a definition but it does not even return a value. What am I missing here?

EDIT: Thanks for the quick replies. Below is a more extensive example using Howard Hinnant's date.h.

#include <iostream>

#include "date/date.h"
//#include <chrono>

//using namespace date;


namespace yv {
using clock_t = std::chrono::system_clock;
using duration_t = std::chrono::duration<double>;
using time_t = std::chrono::time_point<clock_t, duration_t>;

namespace fromnumber {
template<class T, class T_time> T_time time(T const& timestamp) {
    return T_time(timestamp);
}

// Case 1. Correct specialization, not getting any warnings.
template<> std::chrono::time_point<clock_t, duration_t> time(double const& t)
{
    return std::chrono::time_point<clock_t, duration_t>(duration_t(t));
}

// Case 2. Incorrect specialization, compiles and prints the correct datetime but getting a warning
template<> std::chrono::time_point<clock_t, duration_t> time(double const& t)
{
}

// Case 3. Without the specialization it will not compile, error given above


}; // end namespace fromnumber
}; // end namespace yv

std::ostream& operator<< (std::ostream& outStream, const yv::time_t& t) {
    using namespace date;
    auto t2 = date::floor<std::chrono::milliseconds>(t);
    outStream << date::format("%c", t2);
    return outStream;
}


int main()
{


    using namespace yv;
    using namespace std;

    yv::time_t t0 = yv::fromnumber::time<double, yv::time_t>(0.0);
    yv::time_t t1 = yv::fromnumber::time<double, yv::time_t>(1548675254.0);

    cout << t1 << endl;
    // expecting: Mon Jan 28 11:34:14 2019
    return 0;
}

Warning in case 2:

../try_chrono/main.cpp: In function ‘T_time yv::fromnumber::time(const T&) [with T = double; T_time = std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<double> >]’:
../try_chrono/main.cpp:21:1: warning: no return statement in function returning non-void [-Wreturn-type]
 }
 ^

It is clearly undefined behavior to not return a value from a non-void function. However, the thing I do not understand is how it can be possible that I am getting the correct output with an empty specialization? The way I see it is that both case 2 and case 3 are incorrect and should not give me a correct result on stdout.

Undefined behavior. It compiles, you will have a giant warning though, and perhaps a crash. Or nothing.

The thing you know is that you need this definition, and as the definition says, it should return a std::chrono::time_point<clock_t, duration_t>. If you don't then you are breaking your contract. The compiler says so:

warning: no return statement in function returning non-void [-Wreturn-type]

What is the meaning of template<> with empty angle brackets in C , template<> tells the compiler that a template specialization follows, specifically a full specialization. Normally, class A would have to look  A member or a member template of a class template may be explicitly specialized for a given implicit instantiation of the class template, even if the member or member template is defined in the class template definition. Member or a member template may be nested within many enclosing class templates.

The template specialization does not return anything when it is supposed to return std::chrono::time_point<clock_t, duration_t>, resulting in undefined behavior.

The standard clearly states this in [stmt.return]/2 :

Flowing off the end of a value-returning function (except main) without a return statement is undefined behavior.

Template Specialization and Partial Specialization in C++ , How to use template specialization and partial specialization. akin to a template, but this time the list of template parameters will be empty: For example, if you had a templated sortedVector type that required the > operator to be defined,  Partial template specialization is a particular form of class template specialization.Usually used in reference to the C++ programming language, it allows the programmer to specialize only some arguments of a class template, as opposed to explicit full specialization, where all the template arguments are provided.

After stepping through the code with a debugger I found that the empty definition was returning the argument after compilation. The compiler was effectively changing this:

template<> std::chrono::time_point<clock_t, duration_t> time(double t)
{
}

into this:

template<> std::chrono::time_point<clock_t, duration_t> time(double t)
{
return (std::chrono::time_point<clock_t, duration_t>) t;
}

Which in turn resulted in a "correct" binary because an instance of type std::chrono::time_point<clock_t, duration_t> looks like this in memory:

name        value          address
t0                         @0x0123456789ab
    __d                    @0x0123456789ab
        __r 1548675254.02  @0x0123456789ab

so the assignment is performed correctly. However, with a non-empty specialization function without return argument this quirk breaks. For example the following function does not return (std::chrono::time_point<clock_t, duration_t>) t:

template<> std::chrono::time_point<clock_t, duration_t> time(double t)
{
    cout << t << endl;
}

https://stackoverflow.com/a/1610454/2548426 According to this answer the resulting binary depends on platform, architecture and compiler.

As the previous answers have stated, it is undefined behavior. It is now clear me what was causing the apparent correct result.

The correct specialization:

template<> std::chrono::time_point<clock_t, duration_t> time(double t)
{
    return std::chrono::time_point<clock_t, duration_t>(duration_t(t));
}

or

template<> time_t time(double t)
{
    return time_t(duration_t(t));
}

explicit (full) template specialization, We write code once and use it for any data type including user defined data types​. For example, sort() can be written and used to sort any data type items. A class  template specialization. You define members of an explicitly specialized template class as you would normal classes, without the template<>prefix. In addition, you can define the members of an explicit specialization inline; no special template syntax is used in this case. The following example demonstrates a class template specialization:

partial template specialization, For example, to create a template function that returns the greater one of two objects First of all, notice that we precede the class template name with an empty  First of all, notice that we precede the class template name with an empty template<> parameter list. This is to explicitly declare it as a template specialization. But more important than this prefix, is the <char> specialization parameter after the class template name.

Template Specialization in C++, For example, you can define a function template like this: C++ When using a template whose parameters are all defaulted, use empty angle  Each function template has a single function parameter whose type is a specialization of X with template arguments corresponding to the template parameters from the respective function template where, for each template parameter PP in the template parameter list of the function template, a corresponding template argument AA is formed.

Templates - C++ Tutorials, full class template specialization (member definitions in cpp file) ****/. template <​> // empty angle brackets. class AClass<int> // template name  The variadic template in C++ usually starts with the general (empty) definition that also serves as the base-case for template recursion termination in the later specialization:

Comments
  • Undefined Behavior does not mean "will definitely give the wrong result". It just means "anything (including 'correct' behavior) can happen".
  • Thanks for your effort, it is indeed undefined behavior and is highly dependent on platform.
  • I updated my question with a more extensive example. With the empty specialization I am still getting correct results. This does not make sense to me. Is the compiler not able to fill in the correct return type from the call in main()?
  • UB means anything can happen including what is normally expected behavior. No requirements are imposed.