空定义的模板专业化

时间:2019-01-28 12:14:40

标签: c++ chrono function-templates

我做了一个模板函数,用于根据数字初始化chrono::time_point。到目前为止,我已经成功了,但遇到了一个问题,我不完全理解。下面给出了我的代码的两个最小示例。

下面代码失败,错误如下进行编译:

/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;
}

然而,当我添加模板特空定义它编译。<​​/ P>

#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;
}

在专业化具有定义,但它甚至不返回值。我在这里想念什么?

编辑: 感谢您的快速答复。以下是使用Howard Hinnant的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;
}

第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]
 }
 ^

不从非void函数返回值显然是未定义的行为。不过,我不明白的事情是如何可能的是,我得到了正确的输出与空专业化? 我看到它的方式是,这两个壳2和3的情况下是不正确的,不应该给我在stdout正确的结果。

3 个答案:

答案 0 :(得分:3)

未定义的行为。它会编译,但是您将收到一个巨大的警告,甚至可能是崩溃。还是什么都没有。

您知道的是您需要此定义,并且如定义所言,它应返回std::chrono::time_point<clock_t, duration_t>。如果您不这样做,那您就违反了合同。编译器这样说:

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

答案 1 :(得分:3)

模板专门化在应返回std::chrono::time_point<clock_t, duration_t>时不返回任何内容,从而导致行为未定义。

标准在[stmt.return]/2中明确指出了这一点:

  

在没有return语句的情况下从值返回函数的末尾流出(main除外)是未定义的行为。

答案 2 :(得分:0)

通过与调试器我发现空定义返航编译后的参数的代码步进之后。编译器有效地改变了这一点:

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

对此:

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

这又导致了“正确的”二进制,因为类型std::chrono::time_point<clock_t, duration_t>的实例在内存中看起来像这样:

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

因此分配可以正确执行。 然而,没有返回参数此怪癖休息一个非空的专业化功能。 例如,以下函数不返回(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 根据该回答所得的二进制取决于平台,架构和编译器。

如先前的回答所述,这是未定义的行为。现在我很清楚是什么导致了明显的正确结果。

正确的专业化:

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

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