Шаблонная специализация с пустым определением - PullRequest
0 голосов
/ 28 января 2019

Я сделал шаблонную функцию для инициализации 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;
}

Специализация имеет определение, но оно даже не возвращает значение.Что мне здесь не хватает?

РЕДАКТИРОВАТЬ: Спасибо за быстрые ответы.Ниже приведен более подробный пример использования 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]
 }
 ^

Это явно неопределенное поведение, чтобы не возвращать значение из не-пустая функция.Однако, что я не понимаю, так это как возможно, что я получаю правильный вывод с пустой специализацией?Насколько я понимаю, оба случая и случай 3 неверны и не должны давать мне правильный результат на stdout.

Ответы [ 3 ]

0 голосов
/ 28 января 2019

Специализация шаблона ничего не возвращает, когда предполагается, что он должен возвращать std::chrono::time_point<clock_t, duration_t>, что приводит к неопределенному поведению.

Стандарт четко указывает это в [stmt.return] / 2 :

Выход из конца функции, возвращающей значение (кроме main) без оператора return, является неопределенным поведением.

0 голосов
/ 28 января 2019

Пройдя по коду с помощью отладчика, я обнаружил, что пустое определение возвращало аргумент после компиляции.Компилятор фактически изменил это:

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));
}
0 голосов
/ 28 января 2019

Неопределенное поведение.Он компилируется, вы получите гигантское предупреждение, и, возможно, сбой.Или ничего.

То, что вы знаете, это то, что вам нужно это определение, и, как говорится в определении, оно должно возвращать std::chrono::time_point<clock_t, duration_t>.Если вы этого не сделаете, то вы нарушаете свой контракт.Компилятор говорит так:

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