具有空定义的模板专业化
Template specialization with empty definition
我制作了一个模板函数,用于从数字初始化 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;
}
但是,当我添加具有空定义的模板专业化时,它会编译。
#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;
}
专业化有一个定义,但它甚至没有 return 一个值。我在这里错过了什么?
编辑:
感谢您的快速回复。下面是使用 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]
}
^
不return非空函数的值显然是未定义的行为。但是,我不明白的是,我怎么可能通过空专业化获得正确的输出?
我的看法是案例 2 和案例 3 都是不正确的,不应该在标准输出上给我正确的结果。
未定义的行为。它会编译,但你会收到一个巨大的警告,也许还会发生崩溃。或者什么都没有。
你知道的是你需要这个定义,正如定义所说,它应该 return 一个 std::chrono::time_point<clock_t, duration_t>
。如果你不这样做,那么你就是在违约。编译器是这样说的:
warning: no return statement in function returning non-void [-Wreturn-type]
模板特化没有 return 应该 return std::chrono::time_point<clock_t, duration_t>
的任何东西,导致未定义的行为。
标准在 [stmt.return]/2 中明确说明了这一点:
Flowing off the end of a value-returning function (except main) without a return statement is undefined behavior.
在使用调试器单步执行代码后,我发现空定义是 return编译后的参数。编译器有效地改变了这一点:
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;
}
这反过来导致 "correct" 二进制文件,因为 std::chrono::time_point<clock_t, duration_t>
类型的实例在内存中看起来像这样:
name value address
t0 @0x0123456789ab
__d @0x0123456789ab
__r 1548675254.02 @0x0123456789ab
因此分配正确执行。
但是,使用不带 return 参数的非空特化函数,这个怪癖就会被打破。
例如下面的函数不 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;
}
根据这个答案,生成的二进制文件取决于平台、体系结构和编译器。
如前面的答案所述,这是未定义的行为。现在我很清楚是什么导致了明显正确的结果。
正确的专业:
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));
}
我制作了一个模板函数,用于从数字初始化 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;
}
但是,当我添加具有空定义的模板专业化时,它会编译。
#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;
}
专业化有一个定义,但它甚至没有 return 一个值。我在这里错过了什么?
编辑: 感谢您的快速回复。下面是使用 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]
}
^
不return非空函数的值显然是未定义的行为。但是,我不明白的是,我怎么可能通过空专业化获得正确的输出? 我的看法是案例 2 和案例 3 都是不正确的,不应该在标准输出上给我正确的结果。
未定义的行为。它会编译,但你会收到一个巨大的警告,也许还会发生崩溃。或者什么都没有。
你知道的是你需要这个定义,正如定义所说,它应该 return 一个 std::chrono::time_point<clock_t, duration_t>
。如果你不这样做,那么你就是在违约。编译器是这样说的:
warning: no return statement in function returning non-void [-Wreturn-type]
模板特化没有 return 应该 return std::chrono::time_point<clock_t, duration_t>
的任何东西,导致未定义的行为。
标准在 [stmt.return]/2 中明确说明了这一点:
Flowing off the end of a value-returning function (except main) without a return statement is undefined behavior.
在使用调试器单步执行代码后,我发现空定义是 return编译后的参数。编译器有效地改变了这一点:
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;
}
这反过来导致 "correct" 二进制文件,因为 std::chrono::time_point<clock_t, duration_t>
类型的实例在内存中看起来像这样:
name value address
t0 @0x0123456789ab
__d @0x0123456789ab
__r 1548675254.02 @0x0123456789ab
因此分配正确执行。
但是,使用不带 return 参数的非空特化函数,这个怪癖就会被打破。
例如下面的函数不 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;
}
根据这个答案,生成的二进制文件取决于平台、体系结构和编译器。
如前面的答案所述,这是未定义的行为。现在我很清楚是什么导致了明显正确的结果。
正确的专业:
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));
}